This commit is contained in:
Araq 2016-07-08 23:20:31 +02:00
commit c48102254a
72 changed files with 4785 additions and 1791 deletions

View file

@ -395,6 +395,9 @@ type
# sons[1]: field type # sons[1]: field type
# .n: nkDotExpr storing the field name # .n: nkDotExpr storing the field name
tyVoid #\
# now different from tyEmpty, hurray!
static: static:
# remind us when TTypeKind stops to fit in a single 64-bit word # remind us when TTypeKind stops to fit in a single 64-bit word
assert TTypeKind.high.ord <= 63 assert TTypeKind.high.ord <= 63
@ -528,8 +531,6 @@ const
tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \ tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
# 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
# deprecated and this mess can be cleaned up. # deprecated and this mess can be cleaned up.
tfVoid* = tfVarargs # for historical reasons we conflated 'void' with
# 'empty' ('@[]' has the type 'seq[empty]').
tfReturnsNew* = tfInheritable tfReturnsNew* = tfInheritable
skError* = skUnknown skError* = skUnknown
@ -610,7 +611,7 @@ type
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
mNHint, mNWarning, mNError, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo, mNGenSym, mInstantiationInfo, mGetTypeInfo, mNGenSym,
mNimvm mNimvm, mIntDefine, mStrDefine
# things that we can evaluate safely at compile time, even if not asked for it: # things that we can evaluate safely at compile time, even if not asked for it:
const const
@ -1588,7 +1589,7 @@ proc isAtom*(n: PNode): bool {.inline.} =
proc isEmptyType*(t: PType): bool {.inline.} = proc isEmptyType*(t: PType): bool {.inline.} =
## 'void' and 'stmt' types are often equivalent to 'nil' these days: ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
result = t == nil or t.kind in {tyEmpty, tyStmt} result = t == nil or t.kind in {tyVoid, tyStmt}
proc makeStmtList*(n: PNode): PNode = proc makeStmtList*(n: PNode): PNode =
if n.kind == nkStmtList: if n.kind == nkStmtList:

View file

@ -124,7 +124,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
else: else:
internalError("openArrayLoc: " & typeToString(a.t)) internalError("openArrayLoc: " & typeToString(a.t))
else: internalError("openArrayLoc: " & typeToString(a.t)) else: internalError("openArrayLoc: " & typeToString(a.t))
@ -260,7 +260,11 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
else: else:
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else: else:
result = genArgNoParam(p, ri.sons[i]) if tfVarargs notin typ.flags:
localError(ri.info, "wrong argument count")
result = nil
else:
result = genArgNoParam(p, ri.sons[i])
discard """ discard """
Dot call syntax in C++ Dot call syntax in C++

View file

@ -1320,7 +1320,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprAny($1, $2)", [ ropecg(p.module, "#reprAny($1, $2)", [
rdLoc(a), genTypeInfo(p.module, t)]), a.s) rdLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyEmpty: of tyEmpty, tyVoid:
localError(e.info, "'repr' doesn't support 'void' type") localError(e.info, "'repr' doesn't support 'void' type")
else: else:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",

View file

@ -416,7 +416,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
addf(result, "union{$n$1} $2;$n", [unionBody, uname]) addf(result, "union{$n$1} $2;$n", [unionBody, uname])
of nkSym: of nkSym:
field = n.sym field = n.sym
if field.typ.kind == tyEmpty: return if field.typ.kind == tyVoid: return
#assert(field.ast == nil) #assert(field.ast == nil)
sname = mangleRecFieldName(field, rectype) sname = mangleRecFieldName(field, rectype)
if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname] if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname]
@ -663,7 +663,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
chunkStart = i chunkStart = i
let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars) let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars)
if typeInSlot == nil or typeInSlot.kind == tyEmpty: if typeInSlot == nil or typeInSlot.kind == tyVoid:
result.add(~"void") result.add(~"void")
else: else:
result.add getTypeDescAux(m, typeInSlot, check) result.add getTypeDescAux(m, typeInSlot, check)
@ -1022,7 +1022,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
[result, rope(typeToString(t))]) [result, rope(typeToString(t))])
return "(&".rope & result & ")".rope return "(&".rope & result & ")".rope
case t.kind case t.kind
of tyEmpty: result = rope"0" of tyEmpty, tyVoid: result = rope"0"
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
genTypeInfoAuxBase(m, t, t, result, rope"0") genTypeInfoAuxBase(m, t, t, result, rope"0")
of tyProc: of tyProc:

View file

@ -93,7 +93,7 @@ proc getUniqueType*(key: PType): PType =
# produced instead of ``NI``. # produced instead of ``NI``.
result = key result = key
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
tyCString, tyNone, tyBigNum: tyCString, tyNone, tyBigNum, tyVoid:
result = gCanonicalTypes[k] result = gCanonicalTypes[k]
if result == nil: if result == nil:
gCanonicalTypes[k] = key gCanonicalTypes[k] = key

View file

@ -158,7 +158,7 @@ var
enableNotes: TNoteKinds enableNotes: TNoteKinds
disableNotes: TNoteKinds disableNotes: TNoteKinds
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string) = info: TLineInfo; orig: string) =
var id = "" # arg = "X]:on|off" var id = "" # arg = "X]:on|off"
var i = 0 var i = 0
@ -342,7 +342,11 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
discard "allow for backwards compatibility, but don't do anything" discard "allow for backwards compatibility, but don't do anything"
of "define", "d": of "define", "d":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
defineSymbol(arg) if {':', '='} in arg:
splitSwitch(arg, key, val, pass, info)
defineSymbol(key, val)
else:
defineSymbol(arg)
of "undef", "u": of "undef", "u":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
undefSymbol(arg) undefSymbol(arg)

View file

@ -19,8 +19,8 @@ var gSymbols: StringTableRef
const const
catNone = "false" catNone = "false"
proc defineSymbol*(symbol: string) = proc defineSymbol*(symbol: string, value: string = "true") =
gSymbols[symbol] = "true" gSymbols[symbol] = value
proc undefSymbol*(symbol: string) = proc undefSymbol*(symbol: string) =
gSymbols[symbol] = catNone gSymbols[symbol] = catNone
@ -62,6 +62,11 @@ proc isDefined*(symbol: string): bool =
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc lookupSymbol*(symbol: string): string =
result = if isDefined(symbol): gSymbols[symbol] else: nil
proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
iterator definedSymbolNames*: string = iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols): for key, val in pairs(gSymbols):
if val != catNone: yield key if val != catNone: yield key

View file

@ -380,8 +380,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
add(result, literal) add(result, literal)
of tkCurlyDotLe:
dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkCurlyDotRi:
dispA(result, "</div><span class=\"Other pragmaend\">$1</span>",
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkBracketDotLe, tkBracketDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon, tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:

View file

@ -163,7 +163,7 @@ proc mapType(typ: PType): TJSTypeKind =
of tyNil: result = etyNull of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses, tyVoid:
result = etyNone result = etyNone
of tyProc: result = etyProc of tyProc: result = etyProc
of tyCString: result = etyString of tyCString: result = etyString
@ -1215,6 +1215,7 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
genArgNoParam(p, it, r) genArgNoParam(p, it, r)
else: else:
genArg(p, it, paramType.sym, r) genArg(p, it, paramType.sym, r)
inc generated
proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
r: var TCompRes) = r: var TCompRes) =
@ -1557,8 +1558,16 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
of tyEnum, tyOrdinal: of tyEnum, tyOrdinal:
gen(p, n.sons[1], r) gen(p, n.sons[1], r)
useMagic(p, "cstrToNimstr") useMagic(p, "cstrToNimstr")
var offset = ""
if t.kind == tyEnum:
let firstFieldOffset = t.n.sons[0].sym.position
if firstFieldOffset < 0:
offset = "+" & $(-firstFieldOffset)
elif firstFieldOffset > 0:
offset = "-" & $firstFieldOffset
r.kind = resExpr r.kind = resExpr
r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res] r.res = "cstrToNimstr($1.node.sons[$2$3].name)" % [genTypeInfo(p, t), r.res, rope(offset)]
else: else:
# XXX: # XXX:
internalError(n.info, "genRepr: Not implemented") internalError(n.info, "genRepr: Not implemented")

View file

@ -184,8 +184,8 @@ proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} =
# this is called by the semantic checking phase # this is called by the semantic checking phase
result = compileModule(fileIdx, {}) result = compileModule(fileIdx, {})
if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx) if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
if sfSystemModule in result.flags: #if sfSystemModule in result.flags:
localError(result.info, errAttemptToRedefine, result.name.s) # localError(result.info, errAttemptToRedefine, result.name.s)
# restore the notes for outer module: # restore the notes for outer module:
gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
else: ForeignPackageNotes else: ForeignPackageNotes

View file

@ -75,7 +75,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
result = matchNodeKinds(p.constraint, n) result = matchNodeKinds(p.constraint, n)
if not result: return if not result: return
if isNil(n.typ): if isNil(n.typ):
result = p.typ.kind in {tyEmpty, tyStmt} result = p.typ.kind in {tyVoid, tyStmt}
else: else:
result = sigmatch.argtypeMatches(c.c, p.typ, n.typ) result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)

View file

@ -63,7 +63,8 @@ const
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims} wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims} wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
wIntDefine, wStrDefine}
letPragmas* = varPragmas letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
wThread, wRaises, wLocks, wTags, wGcSafe} wThread, wRaises, wLocks, wTags, wGcSafe}
@ -898,6 +899,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wBase: of wBase:
noVal(it) noVal(it)
sym.flags.incl sfBase sym.flags.incl sfBase
of wIntDefine:
sym.magic = mIntDefine
of wStrDefine:
sym.magic = mStrDefine
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else: invalidPragma(it)

View file

@ -704,7 +704,10 @@ proc gcase(g: var TSrcGen, n: PNode) =
proc gproc(g: var TSrcGen, n: PNode) = proc gproc(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
if n.sons[namePos].kind == nkSym: if n.sons[namePos].kind == nkSym:
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym)) let s = n.sons[namePos].sym
put(g, tkSymbol, renderDefinitionName(s))
if sfGenSym in s.flags:
put(g, tkIntLit, $s.id)
else: else:
gsub(g, n.sons[namePos]) gsub(g, n.sons[namePos])
@ -798,7 +801,8 @@ proc gident(g: var TSrcGen, n: PNode) =
else: else:
t = tkOpr t = tkOpr
put(g, t, s) put(g, t, s)
if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id) if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
put(g, tkIntLit, $n.sym.id)
proc doParamsAux(g: var TSrcGen, params: PNode) = proc doParamsAux(g: var TSrcGen, params: PNode) =
if params.len > 1: if params.len > 1:

View file

@ -13,7 +13,7 @@
import import
ast, modules, passes, passaux, condsyms, ast, modules, passes, passaux, condsyms,
options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs, options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
os, times, osproc os, times, osproc, wordrecg
# we support 'cmpIgnoreStyle' natively for efficiency: # we support 'cmpIgnoreStyle' natively for efficiency:
from strutils import cmpIgnoreStyle from strutils import cmpIgnoreStyle
@ -116,7 +116,12 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext =
setResult(a, options.command) setResult(a, options.command)
cbconf switch: cbconf switch:
processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo()) processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
cbconf hintImpl:
processSpecificNote(a.getString 0, wHint, passPP, unknownLineInfo(),
a.getString 1)
cbconf warningImpl:
processSpecificNote(a.getString 0, wWarning, passPP, unknownLineInfo(),
a.getString 1)
proc runNimScript*(scriptName: string; freshDefines=true) = proc runNimScript*(scriptName: string; freshDefines=true) =
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule

View file

@ -307,6 +307,13 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
include hlo, seminst, semcall include hlo, seminst, semcall
when false:
# hopefully not required:
proc resetSemFlag(n: PNode) =
excl n.flags, nfSem
for i in 0 ..< n.safeLen:
resetSemFlag(n[i])
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
## Semantically check the output of a macro. ## Semantically check the output of a macro.
@ -320,6 +327,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
c.friendModules.add(s.owner.getModule) c.friendModules.add(s.owner.getModule)
result = n result = n
excl(n.flags, nfSem)
#resetSemFlag n
if s.typ.sons[0] == nil: if s.typ.sons[0] == nil:
result = semStmt(c, result) result = semStmt(c, result)
else: else:
@ -409,9 +418,6 @@ proc myOpen(module: PSym): PPassContext =
c.importTable.addSym(module) # a module knows itself c.importTable.addSym(module) # a module knows itself
if sfSystemModule in module.flags: if sfSystemModule in module.flags:
magicsys.systemModule = module # set global variable! magicsys.systemModule = module # set global variable!
else:
c.importTable.addSym magicsys.systemModule # import the "System" identifier
importAllSymbols(c, magicsys.systemModule)
c.topLevelScope = openScope(c) c.topLevelScope = openScope(c)
# don't be verbose unless the module belongs to the main package: # don't be verbose unless the module belongs to the main package:
if module.owner.id == gMainPackageId: if module.owner.id == gMainPackageId:
@ -425,7 +431,29 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
result = myOpen(module) result = myOpen(module)
for m in items(rd.methods): methodDef(m, true) for m in items(rd.methods): methodDef(m, true)
proc isImportSystemStmt(n: PNode): bool =
if magicsys.systemModule == nil: return false
case n.kind
of nkImportStmt:
for x in n:
let f = checkModuleName(x)
if f == magicsys.systemModule.info.fileIndex:
return true
of nkImportExceptStmt, nkFromStmt:
let f = checkModuleName(n[0])
if f == magicsys.systemModule.info.fileIndex:
return true
else: discard
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if c.topStmts == 0 and not isImportSystemStmt(n):
if sfSystemModule notin c.module.flags and
n.kind notin {nkEmpty, nkCommentStmt}:
c.importTable.addSym magicsys.systemModule # import the "System" identifier
importAllSymbols(c, magicsys.systemModule)
inc c.topStmts
else:
inc c.topStmts
if sfNoForward in c.module.flags: if sfNoForward in c.module.flags:
result = semAllTypeSections(c, n) result = semAllTypeSections(c, n)
else: else:

View file

@ -183,7 +183,7 @@ proc newSeqCall(c: PContext; x, y: PNode): PNode =
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind case t.kind
of tyNone, tyEmpty: discard of tyNone, tyEmpty, tyVoid: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
tyPtr, tyString, tyRef: tyPtr, tyString, tyRef:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)

View file

@ -99,6 +99,7 @@ type
unknownIdents*: IntSet # ids of all unknown identifiers to prevent unknownIdents*: IntSet # ids of all unknown identifiers to prevent
# naming it multiple times # naming it multiple times
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
topStmts*: int # counts the number of encountered top level statements
lastGenericIdx*: int # used for the generics stack lastGenericIdx*: int # used for the generics stack
hloLoopDetector*: int # used to prevent endless loops in the HLO hloLoopDetector*: int # used to prevent endless loops in the HLO
inParallelStmt*: int inParallelStmt*: int

View file

@ -32,8 +32,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# XXX tyGenericInst here? # XXX tyGenericInst here?
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyEmpty, c) result.typ = newTypeS(tyVoid, c)
result.typ.flags.incl tfVoid
else: else:
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
@ -601,6 +600,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = n result = n
if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
var callee = n.sons[0].sym var callee = n.sons[0].sym
# workaround for bug #537 (overly aggressive inlining leading to
# wrong NimNode semantics):
if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return
# constant folding that is necessary for correctness of semantic pass: # constant folding that is necessary for correctness of semantic pass:
if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil: if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil:

View file

@ -640,6 +640,12 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of mNaN: result = newFloatNodeT(NaN, n) of mNaN: result = newFloatNodeT(NaN, n)
of mInf: result = newFloatNodeT(Inf, n) of mInf: result = newFloatNodeT(Inf, n)
of mNegInf: result = newFloatNodeT(NegInf, n) of mNegInf: result = newFloatNodeT(NegInf, n)
of mIntDefine:
if isDefined(s.name):
result = newIntNodeT(lookupSymbol(s.name).parseInt, n)
of mStrDefine:
if isDefined(s.name):
result = newStrNodeT(lookupSymbol(s.name), n)
else: else:
if sfFakeConst notin s.flags: result = copyTree(s.ast) if sfFakeConst notin s.flags: result = copyTree(s.ast)
of {skProc, skMethod}: of {skProc, skMethod}:

View file

@ -426,7 +426,12 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx) n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
var body: PNode var body: PNode
if n.sons[namePos].kind == nkSym: body = n.sons[namePos].sym.getBody if n.sons[namePos].kind == nkSym:
let s = n.sons[namePos].sym
if sfGenSym in s.flags and s.ast == nil:
body = n.sons[bodyPos]
else:
body = s.getBody
else: body = n.sons[bodyPos] else: body = n.sons[bodyPos]
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx) n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
closeScope(c) closeScope(c)

View file

@ -205,7 +205,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
# The solution would be to move this logic into semtypinst, but # The solution would be to move this logic into semtypinst, but
# at this point semtypinst have to become part of sem, because it # at this point semtypinst have to become part of sem, because it
# will need to use openScope, addDecl, etc. # will need to use openScope, addDecl, etc.
addDecl(c, prc) #addDecl(c, prc)
pushInfoContext(info) pushInfoContext(info)
var cl = initTypeVars(c, pt, info, nil) var cl = initTypeVars(c, pt, info, nil)

View file

@ -138,7 +138,7 @@ proc fixNilType(n: PNode) =
proc discardCheck(c: PContext, result: PNode) = proc discardCheck(c: PContext, result: PNode) =
if c.inTypeClass > 0: return if c.inTypeClass > 0: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if result.kind == nkNilLit: if result.kind == nkNilLit:
result.typ = nil result.typ = nil
message(result.info, warnNilStatement) message(result.info, warnNilStatement)
@ -711,7 +711,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
a.sons[1] = s.typ.n a.sons[1] = s.typ.n
s.typ.size = -1 # could not be computed properly s.typ.size = -1 # could not be computed properly
# we fill it out later. For magic generics like 'seq', it won't be filled # we fill it out later. For magic generics like 'seq', it won't be filled
# so we use tyEmpty instead of nil to not crash for strange conversions # so we use tyNone instead of nil to not crash for strange conversions
# like: mydata.seq # like: mydata.seq
rawAddSon(s.typ, newTypeS(tyNone, c)) rawAddSon(s.typ, newTypeS(tyNone, c))
s.ast = a s.ast = a

View file

@ -945,7 +945,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if isType: localError(a.info, "':' expected") if isType: localError(a.info, "':' expected")
if kind in {skTemplate, skMacro}: if kind in {skTemplate, skMacro}:
typ = newTypeS(tyExpr, c) typ = newTypeS(tyExpr, c)
elif skipTypes(typ, {tyGenericInst}).kind == tyEmpty: elif skipTypes(typ, {tyGenericInst}).kind == tyVoid:
continue continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
@ -977,7 +977,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if r != nil: if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the # turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil': # compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyVoid:
# 'auto' as a return type does not imply a generic: # 'auto' as a return type does not imply a generic:
if r.kind == tyAnything: if r.kind == tyAnything:
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
@ -1390,10 +1390,7 @@ proc processMagicType(c: PContext, m: PSym) =
setMagicType(m, tyTypeDesc, 0) setMagicType(m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType: of mVoidType:
setMagicType(m, tyEmpty, 0) setMagicType(m, tyVoid, 0)
# for historical reasons we conflate 'void' with 'empty' so that '@[]'
# has the type 'seq[void]'.
m.typ.flags.incl tfVoid
of mArray: of mArray:
setMagicType(m, tyArray, 0) setMagicType(m, tyArray, 0)
of mOpenArray: of mOpenArray:

View file

@ -162,7 +162,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
discard discard
of nkSym: of nkSym:
result.sym = replaceTypeVarsS(cl, n.sym) result.sym = replaceTypeVarsS(cl, n.sym)
if result.sym.typ.kind == tyEmpty: if result.sym.typ.kind == tyVoid:
# don't add the 'void' field # don't add the 'void' field
result = newNode(nkRecList, n.info) result = newNode(nkRecList, n.info)
of nkRecWhen: of nkRecWhen:
@ -316,15 +316,15 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
proc eraseVoidParams*(t: PType) = proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really # transform '(): void' into '()' because old parts of the compiler really
# don't deal with '(): void': # don't deal with '(): void':
if t.sons[0] != nil and t.sons[0].kind == tyEmpty: if t.sons[0] != nil and t.sons[0].kind == tyVoid:
t.sons[0] = nil t.sons[0] = nil
for i in 1 .. <t.sonsLen: for i in 1 .. <t.sonsLen:
# don't touch any memory unless necessary # don't touch any memory unless necessary
if t.sons[i].kind == tyEmpty: if t.sons[i].kind == tyVoid:
var pos = i var pos = i
for j in i+1 .. <t.sonsLen: for j in i+1 .. <t.sonsLen:
if t.sons[j].kind != tyEmpty: if t.sons[j].kind != tyVoid:
t.sons[pos] = t.sons[j] t.sons[pos] = t.sons[j]
t.n.sons[pos] = t.n.sons[j] t.n.sons[pos] = t.n.sons[j]
inc pos inc pos

View file

@ -949,8 +949,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isConvertible result = isConvertible
else: discard else: discard
of tyEmpty: of tyEmpty, tyVoid:
if a.kind == tyEmpty: result = isEqual if a.kind == f.kind: result = isEqual
of tyGenericInst: of tyGenericInst:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)

View file

@ -407,7 +407,8 @@ const
"!", "varargs[$1]", "iter[$1]", "Error Type", "!", "varargs[$1]", "iter[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass", "BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "UserTypeClassInst", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"] "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
"void"]
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
@ -727,6 +728,7 @@ proc equalParam(a, b: PSym): TParamsEquality =
result = paramsNotEqual result = paramsNotEqual
proc sameConstraints(a, b: PNode): bool = proc sameConstraints(a, b: PNode): bool =
if isNil(a) and isNil(b): return true
internalAssert a.len == b.len internalAssert a.len == b.len
for i in 1 .. <a.len: for i in 1 .. <a.len:
if not exprStructuralEquivalent(a[i].sym.constraint, if not exprStructuralEquivalent(a[i].sym.constraint,
@ -922,7 +924,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
case a.kind case a.kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyBigNum, tyStmt, tyExpr: tyInt..tyBigNum, tyStmt, tyExpr, tyVoid:
result = sameFlags(a, b) result = sameFlags(a, b)
of tyStatic, tyFromExpr: of tyStatic, tyFromExpr:
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
@ -1108,7 +1110,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = nil result = nil
of tyExpr, tyStmt, tyStatic: of tyExpr, tyStmt, tyStatic:
if kind notin {skParam, skResult}: result = t if kind notin {skParam, skResult}: result = t
of tyEmpty: of tyVoid:
if taField notin flags: result = t if taField notin flags: result = t
of tyTypeClasses: of tyTypeClasses:
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
@ -1153,7 +1155,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
if result != nil: break if result != nil: break
if result.isNil and t.n != nil: if result.isNil and t.n != nil:
result = typeAllowedNode(marker, t.n, kind, flags) result = typeAllowedNode(marker, t.n, kind, flags)
of tyProxy: of tyProxy, tyEmpty:
# for now same as error node; we say it's a valid type as it should # for now same as error node; we say it's a valid type as it should
# prevent cascading errors: # prevent cascading errors:
result = nil result = nil

View file

@ -80,7 +80,7 @@ proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo;
for i in 0 .. < t.len: for i in 0 .. < t.len:
if t.sons[i] == nil: if t.sons[i] == nil:
let void = atomicTypeX("void", mVoid, t, info) let void = atomicTypeX("void", mVoid, t, info)
void.typ = newType(tyEmpty, t.owner) void.typ = newType(tyVoid, t.owner)
result.add void result.add void
else: else:
result.add mapTypeToAstX(t.sons[i], info, inst) result.add mapTypeToAstX(t.sons[i], info, inst)
@ -124,7 +124,8 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
of tyNil: result = atomicType("nil", mNil) of tyNil: result = atomicType("nil", mNil)
of tyExpr: result = atomicType("expr", mExpr) of tyExpr: result = atomicType("expr", mExpr)
of tyStmt: result = atomicType("stmt", mStmt) of tyStmt: result = atomicType("stmt", mStmt)
of tyEmpty: result = atomicType("void", mVoid) of tyVoid: result = atomicType("void", mVoid)
of tyEmpty: result = atomicType("empty", mNone)
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
result.add atomicType("array", mArray) result.add atomicType("array", mArray)

View file

@ -267,7 +267,7 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
# stmt is different from 'void' in meta programming contexts. # stmt is different from 'void' in meta programming contexts.
# So we only set dest to -1 if 'void': # So we only set dest to -1 if 'void':
if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): if dest >= 0 and (n.typ.isNil or n.typ.kind == tyVoid):
c.freeTemp(dest) c.freeTemp(dest)
dest = -1 dest = -1
@ -374,7 +374,7 @@ proc canonValue*(n: PNode): PNode =
proc rawGenLiteral(c: PCtx; n: PNode): int = proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len result = c.constants.len
assert(n.kind != nkCall) #assert(n.kind != nkCall)
n.flags.incl nfAllConst n.flags.incl nfAllConst
c.constants.add n.canonValue c.constants.add n.canonValue
internalAssert result < 0x7fff internalAssert result < 0x7fff
@ -497,12 +497,30 @@ proc genReturn(c: PCtx; n: PNode) =
gen(c, n.sons[0]) gen(c, n.sons[0])
c.gABC(n, opcRet) c.gABC(n, opcRet)
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
# opcLdConst is now always valid. We produce the necessary copy in the
# assignments now:
#var opc = opcLdConst
if dest < 0: dest = c.getTemp(n.typ)
#elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
let lit = genLiteral(c, n)
c.gABx(n, opcLdConst, dest, lit)
proc genCall(c: PCtx; n: PNode; dest: var TDest) = proc genCall(c: PCtx; n: PNode; dest: var TDest) =
# it can happen that due to inlining we have a 'n' that should be
# treated as a constant (see issue #537).
#if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode:
# genLit(c, n, dest)
# return
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
let x = c.getTempRange(n.len, slotTempUnknown) let x = c.getTempRange(n.len, slotTempUnknown)
# varargs need 'opcSetType' for the FFI support: # varargs need 'opcSetType' for the FFI support:
let fntyp = n.sons[0].typ let fntyp = skipTypes(n.sons[0].typ, abstractInst)
for i in 0.. <n.len: for i in 0.. <n.len:
#if i > 0 and i < sonsLen(fntyp):
# let paramType = fntyp.n.sons[i]
# if paramType.typ.isCompileTimeOnly: continue
var r: TRegister = x+i var r: TRegister = x+i
c.gen(n.sons[i], r) c.gen(n.sons[i], r)
if i >= fntyp.len: if i >= fntyp.len:
@ -1307,15 +1325,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
let dest = c.genx(le, {gfAddrOf}) let dest = c.genx(le, {gfAddrOf})
genAsgn(c, dest, ri, requiresCopy) genAsgn(c, dest, ri, requiresCopy)
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
# opcLdConst is now always valid. We produce the necessary copy in the
# assignments now:
#var opc = opcLdConst
if dest < 0: dest = c.getTemp(n.typ)
#elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
let lit = genLiteral(c, n)
c.gABx(n, opcLdConst, dest, lit)
proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
var n = newNode(nkType) var n = newNode(nkType)
n.typ = t n.typ = t
@ -1788,6 +1797,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
genConv(c, n, n.sons[1], dest, opcCast) genConv(c, n, n.sons[1], dest, opcCast)
else: else:
globalError(n.info, errGenerated, "VM is not allowed to 'cast'") globalError(n.info, errGenerated, "VM is not allowed to 'cast'")
of nkTypeOfExpr:
genTypeLit(c, n.typ, dest)
else: else:
globalError(n.info, errGenerated, "cannot generate VM code for " & $n) globalError(n.info, errGenerated, "cannot generate VM code for " & $n)

View file

@ -36,6 +36,7 @@ type
wColon, wColonColon, wEquals, wDot, wDotDot, wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus, wStar, wMinus,
wMagic, wThread, wFinal, wProfiler, wObjChecks, wMagic, wThread, wFinal, wProfiler, wObjChecks,
wIntDefine, wStrDefine,
wDestroy, wDestroy,
@ -121,7 +122,7 @@ const
":", "::", "=", ".", "..", ":", "::", "=", ".", "..",
"*", "-", "*", "-",
"magic", "thread", "final", "profiler", "objchecks", "magic", "thread", "final", "profiler", "objchecks", "intdefine", "strdefine",
"destroy", "destroy",

View file

@ -1,5 +1,5 @@
# This is the config file for the documentation generator. # This is the config file for the documentation generator.
# (c) 2012 Andreas Rumpf # (c) 2016 Andreas Rumpf
# Feel free to edit the templates as you need. If you modify this file, it # Feel free to edit the templates as you need. If you modify this file, it
# might be worth updating the hardcoded values in packages/docutils/rstgen.nim # might be worth updating the hardcoded values in packages/docutils/rstgen.nim
@ -1235,10 +1235,46 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
background-repeat: no-repeat; background-repeat: no-repeat;
background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="); background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
margin-bottom: -5px; } margin-bottom: -5px; }
div.pragma {
display: none;
}
span.pragmabegin {
cursor: pointer;
}
span.pragmaend {
cursor: pointer;
}
</style> </style>
<script type="text/javascript">
function togglepragma(d) {
if (d.style.display != 'inline')
d.style.display = 'inline';
else
d.style.display = 'none';
}
function main() {
var elements = document.getElementsByClassName("pragmabegin");
for (var i = 0; i < elements.length; ++i) {
var e = elements[i];
e.onclick = function(event) {
togglepragma(event.target.nextSibling);
};
}
var elements = document.getElementsByClassName("pragmaend");
for (var i = 0; i < elements.length; ++i) {
var e = elements[i];
e.onclick = function(event) {
togglepragma(event.target.previousSibling);
};
}
}
</script>
</head> </head>
<body> <body onload="main()">
<div class="document" id="documentId"> <div class="document" id="documentId">
<div class="container"> <div class="container">
<h1 class="title">$title</h1> <h1 class="title">$title</h1>

View file

@ -11,7 +11,9 @@ Arguments:
arguments are passed to the program being run (if --run option is selected) arguments are passed to the program being run (if --run option is selected)
Options: Options:
-p, --path:PATH add path to search paths -p, --path:PATH add path to search paths
-d, --define:SYMBOL define a conditional symbol -d, --define:SYMBOL(:VAL)
define a conditional symbol
(Optionally: Define the value for that symbol)
-u, --undef:SYMBOL undefine a conditional symbol -u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules -f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off --stackTrace:on|off turn stack tracing on|off

View file

@ -1011,3 +1011,30 @@ debugging:
# ... complex code here that produces crashes ... # ... complex code here that produces crashes ...
compile time define pragmas
---------------------------
The pragmas listed here can be used to optionally accept values from
the -d/--define option at compile time.
The implementation currently provides the following possible options (various
others may be added later).
=============== ============================================
pragma description
=============== ============================================
intdefine Reads in a build-time define as an integer
strdefine Reads in a build-time define as a string
=============== ============================================
.. code-block:: nim
const FooBar {.intdefine.}: int = 5
echo FooBar
.. code-block:: bash
nim c -d:FooBar=42 foobar.c
In the above example, providing the -d flag causes the symbol
``FooBar`` to be overwritten at compile time, printing out 42. If the
``-d:FooBar=42`` were to be omitted, the default value of 5 would be
used.

View file

@ -215,6 +215,12 @@ the closure and its enclosing scope (i.e. any modifications made to them are
visible in both places). The closure environment may be allocated on the heap visible in both places). The closure environment may be allocated on the heap
or on the stack if the compiler determines that this would be safe. or on the stack if the compiler determines that this would be safe.
Creating closures in loops
~~~~~~~~~~~~~~~~
Since closures capture local variables by reference it is often not wanted
behavior inside loop bodies. See `closureScope <system.html#closureScope>`_
for details on how to change this behavior.
Anonymous Procs Anonymous Procs
--------------- ---------------
@ -223,7 +229,7 @@ Procs can also be treated as expressions, in which case it's allowed to omit
the proc's name. the proc's name.
.. code-block:: nim .. code-block:: nim
var cities = @["Frankfurt", "Tokyo", "New York"] var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
cities.sort(proc (x,y: string): int = cities.sort(proc (x,y: string): int =
cmp(x.len, y.len)) cmp(x.len, y.len))

View file

@ -98,6 +98,11 @@ enable builds in release mode (``-d:release``) where certain safety checks are
omitted for better performance. Another common use is the ``-d:ssl`` switch to omitted for better performance. Another common use is the ``-d:ssl`` switch to
activate `SSL sockets <sockets.html>`_. activate `SSL sockets <sockets.html>`_.
Additionally, you may pass a value along with the symbol: ``-d:x=y``
which may be used in conjunction with the `compile time define
pragmas<manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_
to override symbols during build time.
Configuration files Configuration files
------------------- -------------------
@ -370,7 +375,10 @@ For example, to generate code for an `AVR`:idx: processor use this command::
For the ``standalone`` target one needs to provide For the ``standalone`` target one needs to provide
a file ``panicoverride.nim``. a file ``panicoverride.nim``.
See ``tests/manyloc/standalone/panicoverride.nim`` for an example See ``tests/manyloc/standalone/panicoverride.nim`` for an example
implementation. implementation. Additionally, users should specify the
amount of heap space to use with the ``-d:StandaloneHeapSize=<size>``
command line switch. Note that the total heap size will be
``<size> * sizeof(float64)``.
Nim for realtime systems Nim for realtime systems

View file

@ -102,7 +102,7 @@ __clang__
defined __ICL || \ defined __ICL || \
defined __DMC__ || \ defined __DMC__ || \
defined __BORLANDC__ ) defined __BORLANDC__ )
# define NIM_THREADVAR __declspec(thread) # define NIM_THREADVAR __declspec(thread)
/* note that ICC (linux) and Clang are covered by __GNUC__ */ /* note that ICC (linux) and Clang are covered by __GNUC__ */
#elif defined __GNUC__ || \ #elif defined __GNUC__ || \
defined __SUNPRO_C || \ defined __SUNPRO_C || \
@ -449,8 +449,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
/* Test to see if Nim and the C compiler agree on the size of a pointer. /* Test to see if Nim and the C compiler agree on the size of a pointer.
On disagreement, your C compiler will say something like: On disagreement, your C compiler will say something like:
"error: 'assert_numbits' declared as an array with a negative size" */ "error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -1699,8 +1699,10 @@ proc processBody(node, retFutureSym: NimNode,
else: else:
result.add newCall(newIdentNode("complete"), retFutureSym) result.add newCall(newIdentNode("complete"), retFutureSym)
else: else:
result.add newCall(newIdentNode("complete"), retFutureSym, let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)) if x.kind == nnkYieldStmt: result.add x
else:
result.add newCall(newIdentNode("complete"), retFutureSym, x)
result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt return # Don't process the children of this return stmt
@ -1896,7 +1898,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> createCb(retFuture) # -> createCb(retFuture)
#var cbName = newIdentNode("cb") #var cbName = newIdentNode("cb")
var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym, var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName)) newStrLitNode(prc[0].getName))
outerProcBody.add procCb outerProcBody.add procCb
@ -1931,7 +1933,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
result.add asyncSingleProc(oneProc) result.add asyncSingleProc(oneProc)
else: else:
result = asyncSingleProc(prc) result = asyncSingleProc(prc)
when defined(nimAsyncDebug): when defined(nimDumpAsync):
echo repr result echo repr result
proc recvLine*(socket: AsyncFD): Future[string] {.async.} = proc recvLine*(socket: AsyncFD): Future[string] {.async.} =

View file

@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
## initialized. Example: ## initialized. Example:
## ##
## .. code-block :: ## .. code-block ::
## proc savePreferences(options: Set[string]) = ## proc savePreferences(options: HashSet[string]) =
## assert options.isValid, "Pass an initialized set!" ## assert options.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
result = not s.data.isNil result = not s.data.isNil
@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
## ##
## .. code-block:: ## .. code-block::
## ##
## var values: Set[int] ## var values: HashSet[int]
## assert(not values.isValid) ## assert(not values.isValid)
## assert values.len == 0 ## assert values.len == 0
result = s.counter result = s.counter
@ -338,7 +338,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example: ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
## ##
## .. code-block :: ## .. code-block ::
## var a: Set[int] ## var a: HashSet[int]
## a.init(4) ## a.init(4)
## a.incl(2) ## a.incl(2)
## a.init ## a.init

View file

@ -433,7 +433,7 @@ proc htmlTag*(n: XmlNode): HtmlTag =
proc htmlTag*(s: string): HtmlTag = proc htmlTag*(s: string): HtmlTag =
## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is ## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
## returned. ## returned.
let s = if allLower(s): s else: s.toLower let s = if allLower(s): s else: toLowerAscii(s)
result = toHtmlTag(s) result = toHtmlTag(s)
proc entityToUtf8*(entity: string): string = proc entityToUtf8*(entity: string): string =
@ -513,13 +513,13 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
errors.add(errorMsg(x)) errors.add(errorMsg(x))
next(x) next(x)
of xmlElementStart: of xmlElementStart:
result = newElement(x.elemName.toLower) result = newElement(toLowerAscii(x.elemName))
next(x) next(x)
untilElementEnd(x, result, errors) untilElementEnd(x, result, errors)
of xmlElementEnd: of xmlElementEnd:
errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName)) errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName))
of xmlElementOpen: of xmlElementOpen:
result = newElement(x.elemName.toLower) result = newElement(toLowerAscii(x.elemName))
next(x) next(x)
result.attrs = newStringTable() result.attrs = newStringTable()
while true: while true:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,461 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Eugene Kabanov
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Linux epoll().
import posix, times
# Maximum number of events that can be returned
const MAX_EPOLL_RESULT_EVENTS = 64
type
SignalFdInfo* {.importc: "struct signalfd_siginfo",
header: "<sys/signalfd.h>", pure, final.} = object
ssi_signo*: uint32
ssi_errno*: int32
ssi_code*: int32
ssi_pid*: uint32
ssi_uid*: uint32
ssi_fd*: int32
ssi_tid*: uint32
ssi_band*: uint32
ssi_overrun*: uint32
ssi_trapno*: uint32
ssi_status*: int32
ssi_int*: int32
ssi_ptr*: uint64
ssi_utime*: uint64
ssi_stime*: uint64
ssi_addr*: uint64
pad* {.importc: "__pad".}: array[0..47, uint8]
eventFdData {.importc: "eventfd_t",
header: "<sys/eventfd.h>", pure, final.} = uint64
epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
pure, final.} = object
u64 {.importc: "u64".}: uint64
epoll_event {.importc: "struct epoll_event",
header: "<sys/epoll.h>", pure, final.} = object
events: uint32 # Epoll events
data: epoll_data # User data variable
const
EPOLL_CTL_ADD = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD = 3 # Change file descriptor epoll_event structure.
EPOLLIN = 0x00000001
EPOLLOUT = 0x00000004
EPOLLERR = 0x00000008
EPOLLHUP = 0x00000010
EPOLLRDHUP = 0x00002000
EPOLLONESHOT = 1 shl 30
proc epoll_create(size: cint): cint
{.importc: "epoll_create", header: "<sys/epoll.h>".}
proc epoll_ctl(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint
{.importc: "epoll_ctl", header: "<sys/epoll.h>".}
proc epoll_wait(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint
{.importc: "epoll_wait", header: "<sys/epoll.h>".}
proc timerfd_create(clock_id: ClockId, flags: cint): cint
{.cdecl, importc: "timerfd_create", header: "<sys/timerfd.h>".}
proc timerfd_settime(ufd: cint, flags: cint,
utmr: var Itimerspec, otmr: var Itimerspec): cint
{.cdecl, importc: "timerfd_settime", header: "<sys/timerfd.h>".}
proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
{.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
proc eventfd(count: cuint, flags: cint): cint
{.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
proc ulimit(cmd: cint): clong
{.importc: "ulimit", header: "<ulimit.h>", varargs.}
when hasThreadSupport:
type
SelectorImpl[T] = object
epollFD : cint
maxFD : int
fds: ptr SharedArray[SelectorKey[T]]
count: int
Selector*[T] = ptr SelectorImpl[T]
else:
type
SelectorImpl[T] = object
epollFD : cint
maxFD : int
fds: seq[SelectorKey[T]]
count: int
Selector*[T] = ref SelectorImpl[T]
type
SelectEventImpl = object
efd: cint
SelectEvent* = ptr SelectEventImpl
proc newSelector*[T](): Selector[T] =
var maxFD = int(ulimit(4, 0))
doAssert(maxFD > 0)
var epollFD = epoll_create(MAX_EPOLL_RESULT_EVENTS)
if epollFD < 0:
raiseOsError(osLastError())
when hasThreadSupport:
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
result.epollFD = epollFD
result.maxFD = maxFD
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
else:
result = Selector[T]()
result.epollFD = epollFD
result.maxFD = maxFD
result.fds = newSeq[SelectorKey[T]](maxFD)
proc close*[T](s: Selector[T]) =
if posix.close(s.epollFD) != 0:
raiseOSError(osLastError())
when hasThreadSupport:
deallocSharedArray(s.fds)
deallocShared(cast[pointer](s))
proc newSelectEvent*(): SelectEvent =
let fdci = eventfd(0, 0)
if fdci == -1:
raiseOSError(osLastError())
setNonBlocking(fdci)
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
result.efd = fdci
proc setEvent*(ev: SelectEvent) =
var data : uint64 = 1
if posix.write(ev.efd, addr data, sizeof(uint64)) == -1:
raiseOSError(osLastError())
proc close*(ev: SelectEvent) =
discard posix.close(ev.efd)
deallocShared(cast[pointer](ev))
template checkFd(s, f) =
if f >= s.maxFD:
raise newException(ValueError, "Maximum file descriptors exceeded")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event], data: T) =
let fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
s.setKey(fdi, fdi, events, 0, data)
if events != {}:
var epv = epoll_event(events: EPOLLRDHUP)
epv.data.u64 = fdi.uint
if Event.Read in events: epv.events = epv.events or EPOLLIN
if Event.Write in events: epv.events = epv.events or EPOLLOUT
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
inc(s.count)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error}
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(pkey.events * maskEvents == {})
if pkey.events != events:
var epv = epoll_event(events: EPOLLRDHUP)
epv.data.u64 = fdi.uint
if Event.Read in events: epv.events = epv.events or EPOLLIN
if Event.Write in events: epv.events = epv.events or EPOLLOUT
if pkey.events == {}:
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
inc(s.count)
else:
if events != {}:
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
else:
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
dec(s.count)
pkey.events = events
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
if pkey.events != {}:
if pkey.events * {Event.Read, Event.Write} != {}:
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
dec(s.count)
elif Event.Timer in pkey.events:
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
discard posix.close(fdi.cint)
dec(s.count)
elif Event.Signal in pkey.events:
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
var nmask, omask: Sigset
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, cint(s.fds[fdi].param))
unblockSignals(nmask, omask)
discard posix.close(fdi.cint)
dec(s.count)
elif Event.Process in pkey.events:
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
var nmask, omask: Sigset
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, SIGCHLD)
unblockSignals(nmask, omask)
discard posix.close(fdi.cint)
dec(s.count)
pkey.ident = 0
pkey.events = {}
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.efd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(Event.User in pkey.events)
pkey.ident = 0
pkey.events = {}
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
dec(s.count)
proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
data: T): int {.discardable.} =
var
new_ts: Itimerspec
old_ts: Itimerspec
let fdi = timerfd_create(CLOCK_MONOTONIC, 0).int
if fdi == -1:
raiseOSError(osLastError())
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
var events = {Event.Timer}
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint
if oneshot:
new_ts.it_interval.tv_sec = 0.Time
new_ts.it_interval.tv_nsec = 0
new_ts.it_value.tv_sec = (timeout div 1_000).Time
new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
incl(events, Event.Oneshot)
epv.events = epv.events or EPOLLONESHOT
else:
new_ts.it_interval.tv_sec = (timeout div 1000).Time
new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec
if timerfd_settime(fdi.cint, cint(0), new_ts, old_ts) == -1:
raiseOSError(osLastError())
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
s.setKey(fdi, fdi, events, 0, data)
inc(s.count)
result = fdi
proc registerSignal*[T](s: Selector[T], signal: int,
data: T): int {.discardable.} =
var
nmask: Sigset
omask: Sigset
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, cint(signal))
blockSignals(nmask, omask)
let fdi = signalfd(-1, nmask, 0).int
if fdi == -1:
raiseOSError(osLastError())
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
s.setKey(fdi, signal, {Event.Signal}, signal, data)
inc(s.count)
result = fdi
proc registerProcess*[T](s: Selector, pid: int,
data: T): int {.discardable.} =
var
nmask: Sigset
omask: Sigset
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, posix.SIGCHLD)
blockSignals(nmask, omask)
let fdi = signalfd(-1, nmask, 0).int
if fdi == -1:
raiseOSError(osLastError())
setNonBlocking(fdi.cint)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint
epv.events = EPOLLIN or EPOLLRDHUP
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
inc(s.count)
result = fdi
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = int(ev.efd)
doAssert(s.fds[fdi].ident == 0)
s.setKey(fdi, fdi, {Event.User}, 0, data)
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = ev.efd.uint
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, ev.efd, addr epv) == -1:
raiseOSError(osLastError())
inc(s.count)
proc flush*[T](s: Selector[T]) =
discard
proc selectInto*[T](s: Selector[T], timeout: int,
results: var openarray[ReadyKey[T]]): int =
var
resTable: array[MAX_EPOLL_RESULT_EVENTS, epoll_event]
maxres = MAX_EPOLL_RESULT_EVENTS
events: set[Event] = {}
i, k: int
if maxres > len(results):
maxres = len(results)
let count = epoll_wait(s.epollFD, addr(resTable[0]), maxres.cint,
timeout.cint)
if count < 0:
result = 0
let err = osLastError()
if cint(err) != EINTR:
raiseOSError(err)
elif count == 0:
result = 0
else:
i = 0
k = 0
while i < count:
let fdi = int(resTable[i].data.u64)
let pevents = resTable[i].events
var skey = addr(s.fds[fdi])
doAssert(skey.ident != 0)
events = {}
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
events.incl(Event.Error)
if (pevents and EPOLLOUT) != 0:
events.incl(Event.Write)
if (pevents and EPOLLIN) != 0:
if Event.Read in skey.events:
events.incl(Event.Read)
elif Event.Timer in skey.events:
var data: uint64 = 0
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
raiseOSError(osLastError())
events = {Event.Timer}
elif Event.Signal in skey.events:
var data = SignalFdInfo()
if posix.read(fdi.cint, addr data,
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
raiseOsError(osLastError())
events = {Event.Signal}
elif Event.Process in skey.events:
var data = SignalFdInfo()
if posix.read(fdi.cint, addr data,
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
raiseOsError(osLastError())
if cast[int](data.ssi_pid) == skey.param:
events = {Event.Process}
else:
inc(i)
continue
elif Event.User in skey.events:
var data: uint = 0
if posix.read(fdi.cint, addr data, sizeof(uint)) != sizeof(uint):
let err = osLastError()
if err == OSErrorCode(EAGAIN):
inc(i)
continue
else:
raiseOSError(err)
events = {Event.User}
skey.key.events = events
results[k] = skey.key
inc(k)
if Event.Oneshot in skey.events:
var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
raiseOSError(osLastError())
discard posix.close(fdi.cint)
skey.ident = 0
skey.events = {}
dec(s.count)
inc(i)
result = k
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
result = newSeq[ReadyKey[T]](MAX_EPOLL_RESULT_EVENTS)
let count = selectInto(s, timeout, result)
result.setLen(count)
template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
template withData*[T](s: Selector[T], fd: SocketHandle, value,
body: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
body2: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body1
else:
body2

View file

@ -0,0 +1,439 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Eugene Kabanov
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements BSD kqueue().
import posix, times, kqueue
const
# Maximum number of cached changes.
MAX_KQUEUE_CHANGE_EVENTS = 64
# Maximum number of events that can be returned.
MAX_KQUEUE_RESULT_EVENTS = 64
when defined(macosx) or defined(freebsd):
when defined(macosx):
const MAX_DESCRIPTORS_ID = 29 # KERN_MAXFILESPERPROC (MacOS)
else:
const MAX_DESCRIPTORS_ID = 27 # KERN_MAXFILESPERPROC (FreeBSD)
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
newp: pointer, newplen: int): cint
{.importc: "sysctl",header: """#include <sys/types.h>
#include <sys/sysctl.h>"""}
elif defined(netbsd) or defined(openbsd):
# OpenBSD and NetBSD don't have KERN_MAXFILESPERPROC, so we are using
# KERN_MAXFILES, because KERN_MAXFILES is always bigger,
# than KERN_MAXFILESPERPROC.
const MAX_DESCRIPTORS_ID = 7 # KERN_MAXFILES
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int,
newp: pointer, newplen: int): cint
{.importc: "sysctl",header: """#include <sys/param.h>
#include <sys/sysctl.h>"""}
when hasThreadSupport:
type
SelectorImpl[T] = object
kqFD : cint
maxFD : int
changesTable: array[MAX_KQUEUE_CHANGE_EVENTS, KEvent]
changesCount: int
fds: ptr SharedArray[SelectorKey[T]]
count: int
changesLock: Lock
Selector*[T] = ptr SelectorImpl[T]
else:
type
SelectorImpl[T] = object
kqFD : cint
maxFD : int
changesTable: array[MAX_KQUEUE_CHANGE_EVENTS, KEvent]
changesCount: int
fds: seq[SelectorKey[T]]
count: int
Selector*[T] = ref SelectorImpl[T]
type
SelectEventImpl = object
rfd: cint
wfd: cint
# SelectEvent is declared as `ptr` to be placed in `shared memory`,
# so you can share one SelectEvent handle between threads.
type SelectEvent* = ptr SelectEventImpl
proc newSelector*[T](): Selector[T] =
var maxFD = 0.cint
var size = sizeof(cint)
var namearr = [1.cint, MAX_DESCRIPTORS_ID.cint]
# Obtain maximum number of file descriptors for process
if sysctl(addr(namearr[0]), 2, cast[pointer](addr maxFD), addr size,
nil, 0) != 0:
raiseOsError(osLastError())
var kqFD = kqueue()
if kqFD < 0:
raiseOsError(osLastError())
when hasThreadSupport:
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
result.kqFD = kqFD
result.maxFD = maxFD.int
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
initLock(result.changesLock)
else:
result = Selector[T]()
result.kqFD = kqFD
result.maxFD = maxFD.int
result.fds = newSeq[SelectorKey[T]](maxFD)
proc close*[T](s: Selector[T]) =
if posix.close(s.kqFD) != 0:
raiseOSError(osLastError())
when hasThreadSupport:
deinitLock(s.changesLock)
deallocSharedArray(s.fds)
deallocShared(cast[pointer](s))
proc newSelectEvent*(): SelectEvent =
var fds: array[2, cint]
if posix.pipe(fds) == -1:
raiseOSError(osLastError())
setNonBlocking(fds[0])
setNonBlocking(fds[1])
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
result.rfd = fds[0]
result.wfd = fds[1]
proc setEvent*(ev: SelectEvent) =
var data: uint64 = 1
if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
raiseOSError(osLastError())
proc close*(ev: SelectEvent) =
discard posix.close(cint(ev.rfd))
discard posix.close(cint(ev.wfd))
deallocShared(cast[pointer](ev))
template checkFd(s, f) =
if f >= s.maxFD:
raise newException(ValueError, "Maximum file descriptors exceeded")
when hasThreadSupport:
template withChangeLock[T](s: Selector[T], body: untyped) =
acquire(s.changesLock)
{.locks: [s.changesLock].}:
try:
body
finally:
release(s.changesLock)
else:
template withChangeLock(s, body: untyped) =
body
template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort,
nflags: cushort, nfflags: cuint, ndata: int,
nudata: pointer) =
mixin withChangeLock
s.withChangeLock():
s.changesTable[s.changesCount] = KEvent(ident: nident,
filter: nfilter, flags: nflags,
fflags: nfflags, data: ndata,
udata: nudata)
inc(s.changesCount)
if s.changesCount == MAX_KQUEUE_CHANGE_EVENTS:
if kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
nil, 0, nil) == -1:
raiseOSError(osLastError())
s.changesCount = 0
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event], data: T) =
let fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
s.setKey(fdi, fdi, events, 0, data)
if events != {}:
if Event.Read in events:
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
inc(s.count)
if Event.Write in events:
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_ADD, 0, 0, nil)
inc(s.count)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error}
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(pkey.events * maskEvents == {})
if pkey.events != events:
if (Event.Read in pkey.events) and (Event.Read notin events):
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
dec(s.count)
if (Event.Write in pkey.events) and (Event.Write notin events):
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_DELETE, 0, 0, nil)
dec(s.count)
if (Event.Read notin pkey.events) and (Event.Read in events):
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
inc(s.count)
if (Event.Write notin pkey.events) and (Event.Write in events):
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_ADD, 0, 0, nil)
inc(s.count)
pkey.events = events
proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
data: T): int {.discardable.} =
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
posix.IPPROTO_TCP).int
if fdi == -1:
raiseOsError(osLastError())
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
s.setKey(fdi, fdi, events, 0, data)
# EVFILT_TIMER on Open/Net(BSD) has granularity of only milliseconds,
# but MacOS and FreeBSD allow use `0` as `fflags` to use milliseconds
# too
modifyKQueue(s, fdi.uint, EVFILT_TIMER, flags, 0, cint(timeout), nil)
inc(s.count)
result = fdi
proc registerSignal*[T](s: Selector[T], signal: int,
data: T): int {.discardable.} =
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
posix.IPPROTO_TCP).int
if fdi == -1:
raiseOsError(osLastError())
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
s.setKey(fdi, signal, {Event.Signal}, signal, data)
var nmask, omask: Sigset
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, cint(signal))
blockSignals(nmask, omask)
# to be compatible with linux semantic we need to "eat" signals
posix.signal(cint(signal), SIG_IGN)
modifyKQueue(s, signal.uint, EVFILT_SIGNAL, EV_ADD, 0, 0,
cast[pointer](fdi))
inc(s.count)
result = fdi
proc registerProcess*[T](s: Selector[T], pid: int,
data: T): int {.discardable.} =
var fdi = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
posix.IPPROTO_TCP).int
if fdi == -1:
raiseOsError(osLastError())
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
var kflags: cushort = EV_ONESHOT or EV_ADD
setKey(s, fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
modifyKQueue(s, pid.uint, EVFILT_PROC, kflags, NOTE_EXIT, 0,
cast[pointer](fdi))
inc(s.count)
result = fdi
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = ev.rfd.int
doAssert(s.fds[fdi].ident == 0)
setKey(s, fdi, fdi, {Event.User}, 0, data)
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
inc(s.count)
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
if pkey.events != {}:
if pkey.events * {Event.Read, Event.Write} != {}:
if Event.Read in pkey.events:
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
dec(s.count)
if Event.Write in pkey.events:
modifyKQueue(s, fdi.uint, EVFILT_WRITE, EV_DELETE, 0, 0, nil)
dec(s.count)
elif Event.Timer in pkey.events:
discard posix.close(cint(pkey.key.fd))
modifyKQueue(s, fdi.uint, EVFILT_TIMER, EV_DELETE, 0, 0, nil)
dec(s.count)
elif Event.Signal in pkey.events:
var nmask, omask: Sigset
var signal = cint(pkey.param)
discard sigemptyset(nmask)
discard sigemptyset(omask)
discard sigaddset(nmask, signal)
unblockSignals(nmask, omask)
posix.signal(signal, SIG_DFL)
discard posix.close(cint(pkey.key.fd))
modifyKQueue(s, fdi.uint, EVFILT_SIGNAL, EV_DELETE, 0, 0, nil)
dec(s.count)
elif Event.Process in pkey.events:
discard posix.close(cint(pkey.key.fd))
modifyKQueue(s, fdi.uint, EVFILT_PROC, EV_DELETE, 0, 0, nil)
dec(s.count)
elif Event.User in pkey.events:
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
dec(s.count)
pkey.ident = 0
pkey.events = {}
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(Event.User in pkey.events)
pkey.ident = 0
pkey.events = {}
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil)
dec(s.count)
proc flush*[T](s: Selector[T]) =
s.withChangeLock():
var tv = Timespec()
if kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
nil, 0, addr tv) == -1:
raiseOSError(osLastError())
s.changesCount = 0
proc selectInto*[T](s: Selector[T], timeout: int,
results: var openarray[ReadyKey[T]]): int =
var
tv: Timespec
resTable: array[MAX_KQUEUE_RESULT_EVENTS, KEvent]
ptv = addr tv
maxres = MAX_KQUEUE_RESULT_EVENTS
if timeout != -1:
if timeout >= 1000:
tv.tv_sec = (timeout div 1_000).Time
tv.tv_nsec = (timeout %% 1_000) * 1_000_000
else:
tv.tv_sec = 0.Time
tv.tv_nsec = timeout * 1_000_000
else:
ptv = nil
if maxres > len(results):
maxres = len(results)
var count = 0
s.withChangeLock():
count = kevent(s.kqFD, addr(s.changesTable[0]), cint(s.changesCount),
addr(resTable[0]), cint(maxres), ptv)
s.changesCount = 0
if count < 0:
result = 0
let err = osLastError()
if cint(err) != EINTR:
raiseOSError(err)
elif count == 0:
result = 0
else:
var i = 0
var k = 0
var pkey: ptr SelectorKey[T]
while i < count:
let kevent = addr(resTable[i])
if (kevent.flags and EV_ERROR) == 0:
case kevent.filter:
of EVFILT_READ:
pkey = addr(s.fds[kevent.ident.int])
pkey.key.events = {Event.Read}
if Event.User in pkey.events:
var data: uint64 = 0
if posix.read(kevent.ident.cint, addr data,
sizeof(uint64)) != sizeof(uint64):
let err = osLastError()
if err == OSErrorCode(EAGAIN):
# someone already consumed event data
inc(i)
continue
else:
raiseOSError(osLastError())
pkey.key.events = {Event.User}
of EVFILT_WRITE:
pkey = addr(s.fds[kevent.ident.int])
pkey.key.events = {Event.Write}
of EVFILT_TIMER:
pkey = addr(s.fds[kevent.ident.int])
if Event.Oneshot in pkey.events:
if posix.close(cint(pkey.ident)) == -1:
raiseOSError(osLastError())
pkey.ident = 0
pkey.events = {}
dec(s.count)
pkey.key.events = {Event.Timer}
of EVFILT_VNODE:
pkey = addr(s.fds[kevent.ident.int])
pkey.key.events = {Event.Vnode}
of EVFILT_SIGNAL:
pkey = addr(s.fds[cast[int](kevent.udata)])
pkey.key.events = {Event.Signal}
of EVFILT_PROC:
pkey = addr(s.fds[cast[int](kevent.udata)])
if posix.close(cint(pkey.ident)) == -1:
raiseOSError(osLastError())
pkey.ident = 0
pkey.events = {}
dec(s.count)
pkey.key.events = {Event.Process}
else:
raise newException(ValueError, "Unsupported kqueue filter in queue")
if (kevent.flags and EV_EOF) != 0:
pkey.key.events.incl(Event.Error)
results[k] = pkey.key
inc(k)
inc(i)
result = k
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
result = newSeq[ReadyKey[T]](MAX_KQUEUE_RESULT_EVENTS)
let count = selectInto(s, timeout, result)
result.setLen(count)
template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
template withData*[T](s: Selector[T], fd: SocketHandle, value,
body: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
body2: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body1
else:
body2

View file

@ -0,0 +1,295 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Eugene Kabanov
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Posix poll().
import posix, times
# Maximum number of events that can be returned
const MAX_POLL_RESULT_EVENTS = 64
when hasThreadSupport:
type
SelectorImpl[T] = object
maxFD : int
pollcnt: int
fds: ptr SharedArray[SelectorKey[T]]
pollfds: ptr SharedArray[TPollFd]
count: int
lock: Lock
Selector*[T] = ptr SelectorImpl[T]
else:
type
SelectorImpl[T] = object
maxFD : int
pollcnt: int
fds: seq[SelectorKey[T]]
pollfds: seq[TPollFd]
count: int
Selector*[T] = ref SelectorImpl[T]
type
SelectEventImpl = object
rfd: cint
wfd: cint
SelectEvent* = ptr SelectEventImpl
var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
header: "<sys/resource.h>".}: cint
type
rlimit {.importc: "struct rlimit",
header: "<sys/resource.h>", pure, final.} = object
rlim_cur: int
rlim_max: int
proc getrlimit(resource: cint, rlp: var rlimit): cint
{.importc: "getrlimit",header: "<sys/resource.h>".}
when hasThreadSupport:
template withPollLock[T](s: Selector[T], body: untyped) =
acquire(s.lock)
{.locks: [s.lock].}:
try:
body
finally:
release(s.lock)
else:
template withPollLock(s, body: untyped) =
body
proc newSelector*[T](): Selector[T] =
var a = rlimit()
if getrlimit(RLIMIT_NOFILE, a) != 0:
raiseOsError(osLastError())
var maxFD = int(a.rlim_max)
when hasThreadSupport:
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
result.maxFD = maxFD
result.fds = allocSharedArray[SelectorKey[T]](maxFD)
result.pollfds = allocSharedArray[TPollFd](maxFD)
initLock(result.lock)
else:
result = Selector[T]()
result.maxFD = maxFD
result.fds = newSeq[SelectorKey[T]](maxFD)
result.pollfds = newSeq[TPollFd](maxFD)
proc close*[T](s: Selector[T]) =
when hasThreadSupport:
deinitLock(s.lock)
deallocSharedArray(s.fds)
deallocSharedArray(s.pollfds)
deallocShared(cast[pointer](s))
template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
withPollLock(s):
var pollev: cshort = 0
if Event.Read in events: pollev = pollev or POLLIN
if Event.Write in events: pollev = pollev or POLLOUT
s.pollfds[s.pollcnt].fd = cint(sock)
s.pollfds[s.pollcnt].events = pollev
inc(s.count)
inc(s.pollcnt)
template pollUpdate[T](s: Selector[T], sock: cint, events: set[Event]) =
withPollLock(s):
var i = 0
var pollev: cshort = 0
if Event.Read in events: pollev = pollev or POLLIN
if Event.Write in events: pollev = pollev or POLLOUT
while i < s.pollcnt:
if s.pollfds[i].fd == sock:
s.pollfds[i].events = pollev
break
inc(i)
if i == s.pollcnt:
raise newException(ValueError, "Descriptor is not registered in queue")
template pollRemove[T](s: Selector[T], sock: cint) =
withPollLock(s):
var i = 0
while i < s.pollcnt:
if s.pollfds[i].fd == sock:
if i == s.pollcnt - 1:
s.pollfds[i].fd = 0
s.pollfds[i].events = 0
s.pollfds[i].revents = 0
else:
while i < (s.pollcnt - 1):
s.pollfds[i].fd = s.pollfds[i + 1].fd
s.pollfds[i].events = s.pollfds[i + 1].events
inc(i)
break
inc(i)
dec(s.pollcnt)
dec(s.count)
template checkFd(s, f) =
if f >= s.maxFD:
raise newException(ValueError, "Maximum file descriptors exceeded")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event], data: T) =
var fdi = int(fd)
s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0)
s.setKey(fdi, fdi, events, 0, data)
if events != {}: s.pollAdd(fdi.cint, events)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error}
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(pkey.events * maskEvents == {})
if pkey.events != events:
if pkey.events == {}:
s.pollAdd(fd.cint, events)
else:
if events != {}:
s.pollUpdate(fd.cint, events)
else:
s.pollRemove(fd.cint)
pkey.events = events
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
var fdi = int(ev.rfd)
doAssert(s.fds[fdi].ident == 0)
var events = {Event.User}
setKey(s, fdi, fdi, events, 0, data)
events.incl(Event.Read)
s.pollAdd(fdi.cint, events)
proc flush*[T](s: Selector[T]) = discard
proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
pkey.ident = 0
pkey.events = {}
s.pollRemove(fdi.cint)
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd)
s.checkFd(fdi)
var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0)
doAssert(Event.User in pkey.events)
pkey.ident = 0
pkey.events = {}
s.pollRemove(fdi.cint)
proc newSelectEvent*(): SelectEvent =
var fds: array[2, cint]
if posix.pipe(fds) == -1:
raiseOSError(osLastError())
setNonBlocking(fds[0])
setNonBlocking(fds[1])
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
result.rfd = fds[0]
result.wfd = fds[1]
proc setEvent*(ev: SelectEvent) =
var data: uint64 = 1
if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
raiseOSError(osLastError())
proc close*(ev: SelectEvent) =
discard posix.close(cint(ev.rfd))
discard posix.close(cint(ev.wfd))
deallocShared(cast[pointer](ev))
proc selectInto*[T](s: Selector[T], timeout: int,
results: var openarray[ReadyKey[T]]): int =
var maxres = MAX_POLL_RESULT_EVENTS
if maxres > len(results):
maxres = len(results)
s.withPollLock():
let count = posix.poll(addr(s.pollfds[0]), Tnfds(s.pollcnt), timeout)
if count < 0:
result = 0
let err = osLastError()
if err.cint == EINTR:
discard
else:
raiseOSError(osLastError())
elif count == 0:
result = 0
else:
var i = 0
var k = 0
var rindex = 0
while (i < s.pollcnt) and (k < count) and (rindex < maxres):
let revents = s.pollfds[i].revents
if revents != 0:
let fd = s.pollfds[i].fd
var skey = addr(s.fds[fd])
skey.key.events = {}
if (revents and POLLIN) != 0:
skey.key.events.incl(Event.Read)
if Event.User in skey.events:
var data: uint64 = 0
if posix.read(fd, addr data, sizeof(int)) != sizeof(int):
let err = osLastError()
if err != OSErrorCode(EAGAIN):
raiseOSError(osLastError())
else:
# someone already consumed event data
inc(i)
continue
skey.key.events = {Event.User}
if (revents and POLLOUT) != 0:
skey.key.events.incl(Event.Write)
if (revents and POLLERR) != 0 or (revents and POLLHUP) != 0 or
(revents and POLLNVAL) != 0:
skey.key.events.incl(Event.Error)
results[rindex] = skey.key
s.pollfds[i].revents = 0
inc(rindex)
inc(k)
inc(i)
result = k
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
result = newSeq[ReadyKey[T]](MAX_POLL_RESULT_EVENTS)
let count = selectInto(s, timeout, result)
result.setLen(count)
template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
template withData*[T](s: Selector[T], fd: SocketHandle, value,
body: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body
template withData*[T](s: Selector[T], fd: SocketHandle, value, body1,
body2: untyped) =
mixin checkFd
let fdi = int(fd)
s.checkFd(fdi)
if s.fds[fdi].ident != 0:
var value = addr(s.fds[fdi].key.data)
body1
else:
body2

View file

@ -0,0 +1,416 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Eugene Kabanov
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Posix and Windows select().
import times, nativesockets
when defined(windows):
import winlean
when defined(gcc):
{.passL: "-lws2_32".}
elif defined(vcc):
{.passL: "ws2_32.lib".}
const platformHeaders = """#include <winsock2.h>
#include <windows.h>"""
const EAGAIN = WSAEWOULDBLOCK
else:
const platformHeaders = """#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>"""
type
Fdset {.importc: "fd_set", header: platformHeaders, pure, final.} = object
var
FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint
proc IOFD_SET(fd: SocketHandle, fdset: ptr Fdset)
{.cdecl, importc: "FD_SET", header: platformHeaders, inline.}
proc IOFD_CLR(fd: SocketHandle, fdset: ptr Fdset)
{.cdecl, importc: "FD_CLR", header: platformHeaders, inline.}
proc IOFD_ZERO(fdset: ptr Fdset)
{.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.}
when defined(windows):
proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
{.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.}
proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
timeout: ptr Timeval): cint
{.stdcall, importc: "select", header: platformHeaders.}
else:
proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
{.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.}
proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
timeout: ptr Timeval): cint
{.cdecl, importc: "select", header: platformHeaders.}
when hasThreadSupport:
type
SelectorImpl[T] = object
rSet: FdSet
wSet: FdSet
eSet: FdSet
maxFD: int
fds: ptr SharedArray[SelectorKey[T]]
count: int
lock: Lock
Selector*[T] = ptr SelectorImpl[T]
else:
type
SelectorImpl[T] = object
rSet: FdSet
wSet: FdSet
eSet: FdSet
maxFD: int
fds: seq[SelectorKey[T]]
count: int
Selector*[T] = ref SelectorImpl[T]
type
SelectEventImpl = object
rsock: SocketHandle
wsock: SocketHandle
SelectEvent* = ptr SelectEventImpl
when hasThreadSupport:
template withSelectLock[T](s: Selector[T], body: untyped) =
acquire(s.lock)
{.locks: [s.lock].}:
try:
body
finally:
release(s.lock)
else:
template withSelectLock[T](s: Selector[T], body: untyped) =
body
proc newSelector*[T](): Selector[T] =
when hasThreadSupport:
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
result.fds = allocSharedArray[SelectorKey[T]](FD_SETSIZE)
initLock result.lock
else:
result = Selector[T]()
result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
IOFD_ZERO(addr result.rSet)
IOFD_ZERO(addr result.wSet)
IOFD_ZERO(addr result.eSet)
proc close*[T](s: Selector[T]) =
when hasThreadSupport:
deallocSharedArray(s.fds)
deallocShared(cast[pointer](s))
when defined(windows):
proc newSelectEvent*(): SelectEvent =
var ssock = newNativeSocket()
var wsock = newNativeSocket()
var rsock: SocketHandle = INVALID_SOCKET
var saddr = Sockaddr_in()
saddr.sin_family = winlean.AF_INET
saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(ssock, cast[ptr SockAddr](addr(saddr)),
sizeof(saddr).SockLen) < 0'i32:
raiseOSError(osLastError())
if winlean.listen(ssock, 1) == -1:
raiseOSError(osLastError())
var namelen = sizeof(saddr).SockLen
if getsockname(ssock, cast[ptr SockAddr](addr(saddr)),
addr(namelen)) == -1'i32:
raiseOSError(osLastError())
saddr.sin_addr.s_addr = 0x0100007F
if winlean.connect(wsock, cast[ptr SockAddr](addr(saddr)),
sizeof(saddr).SockLen) == -1:
raiseOSError(osLastError())
namelen = sizeof(saddr).SockLen
rsock = winlean.accept(ssock, cast[ptr SockAddr](addr(saddr)),
cast[ptr SockLen](addr(namelen)))
if rsock == SocketHandle(-1):
raiseOSError(osLastError())
if winlean.closesocket(ssock) == -1:
raiseOSError(osLastError())
var mode = clong(1)
if ioctlsocket(rsock, FIONBIO, addr(mode)) == -1:
raiseOSError(osLastError())
mode = clong(1)
if ioctlsocket(wsock, FIONBIO, addr(mode)) == -1:
raiseOSError(osLastError())
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
result.rsock = rsock
result.wsock = wsock
proc setEvent*(ev: SelectEvent) =
var data: int = 1
if winlean.send(ev.wsock, cast[pointer](addr data),
cint(sizeof(int)), 0) != sizeof(int):
raiseOSError(osLastError())
proc close*(ev: SelectEvent) =
discard winlean.closesocket(ev.rsock)
discard winlean.closesocket(ev.wsock)
deallocShared(cast[pointer](ev))
else:
proc newSelectEvent*(): SelectEvent =
var fds: array[2, cint]
if posix.pipe(fds) == -1:
raiseOSError(osLastError())
setNonBlocking(fds[0])
setNonBlocking(fds[1])
result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
result.rsock = SocketHandle(fds[0])
result.wsock = SocketHandle(fds[1])
proc setEvent*(ev: SelectEvent) =
var data: uint64 = 1
if posix.write(cint(ev.wsock), addr data, sizeof(uint64)) != sizeof(uint64):
raiseOSError(osLastError())
proc close*(ev: SelectEvent) =
discard posix.close(cint(ev.rsock))
discard posix.close(cint(ev.wsock))
deallocShared(cast[pointer](ev))
proc setKey[T](s: Selector[T], fd: SocketHandle, events: set[Event], data: T) =
var i = 0
let fdi = int(fd)
while i < FD_SETSIZE:
if s.fds[i].ident == 0:
var pkey = addr(s.fds[i])
pkey.ident = fdi
pkey.events = events
pkey.key.fd = fd.int
pkey.key.events = {}
pkey.key.data = data
break
inc(i)
if i == FD_SETSIZE:
raise newException(ValueError, "Maximum numbers of fds exceeded")
proc getKey[T](s: Selector[T], fd: SocketHandle): ptr SelectorKey[T] =
var i = 0
let fdi = int(fd)
while i < FD_SETSIZE:
if s.fds[i].ident == fdi:
result = addr(s.fds[i])
break
inc(i)
doAssert(i < FD_SETSIZE, "Descriptor not registered in queue")
proc delKey[T](s: Selector[T], fd: SocketHandle) =
var i = 0
while i < FD_SETSIZE:
if s.fds[i].ident == fd.int:
s.fds[i].ident = 0
s.fds[i].events = {}
break
inc(i)
doAssert(i < FD_SETSIZE, "Descriptor not registered in queue")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event], data: T) =
when not defined(windows):
let fdi = int(fd)
s.withSelectLock():
s.setKey(fd, events, data)
when not defined(windows):
if fdi > s.maxFD: s.maxFD = fdi
if Event.Read in events:
IOFD_SET(fd, addr s.rSet)
inc(s.count)
if Event.Write in events:
IOFD_SET(fd, addr s.wSet)
IOFD_SET(fd, addr s.eSet)
inc(s.count)
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
when not defined(windows):
let fdi = int(ev.rsock)
s.withSelectLock():
s.setKey(ev.rsock, {Event.User}, data)
when not defined(windows):
if fdi > s.maxFD: s.maxFD = fdi
IOFD_SET(ev.rsock, addr s.rSet)
inc(s.count)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error}
s.withSelectLock():
var pkey = s.getKey(fd)
doAssert(pkey.events * maskEvents == {})
if pkey.events != events:
if (Event.Read in pkey.events) and (Event.Read notin events):
IOFD_CLR(fd, addr s.rSet)
dec(s.count)
if (Event.Write in pkey.events) and (Event.Write notin events):
IOFD_CLR(fd, addr s.wSet)
IOFD_CLR(fd, addr s.eSet)
dec(s.count)
if (Event.Read notin pkey.events) and (Event.Read in events):
IOFD_SET(fd, addr s.rSet)
inc(s.count)
if (Event.Write notin pkey.events) and (Event.Write in events):
IOFD_SET(fd, addr s.wSet)
IOFD_SET(fd, addr s.eSet)
inc(s.count)
pkey.events = events
proc unregister*[T](s: Selector[T], fd: SocketHandle) =
s.withSelectLock():
var pkey = s.getKey(fd)
if Event.Read in pkey.events:
IOFD_CLR(fd, addr s.rSet)
dec(s.count)
if Event.Write in pkey.events:
IOFD_CLR(fd, addr s.wSet)
IOFD_CLR(fd, addr s.eSet)
dec(s.count)
s.delKey(fd)
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fd = ev.rsock
s.withSelectLock():
IOFD_CLR(fd, addr s.rSet)
dec(s.count)
s.delKey(fd)
proc selectInto*[T](s: Selector[T], timeout: int,
results: var openarray[ReadyKey[T]]): int =
var tv = Timeval()
var ptv = addr tv
var rset, wset, eset: FdSet
if timeout != -1:
tv.tv_sec = timeout.int32 div 1_000
tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
else:
ptv = nil
s.withSelectLock():
rset = s.rSet
wset = s.wSet
eset = s.eSet
var count = ioselect(cint(s.maxFD) + 1, addr(rset), addr(wset),
addr(eset), ptv)
if count < 0:
result = 0
when defined(windows):
raiseOSError(osLastError())
else:
let err = osLastError()
if cint(err) != EINTR:
raiseOSError(err)
elif count == 0:
result = 0
else:
var rindex = 0
var i = 0
var k = 0
while (i < FD_SETSIZE) and (k < count):
if s.fds[i].ident != 0:
var flag = false
var pkey = addr(s.fds[i])
pkey.key.events = {}
let fd = SocketHandle(pkey.ident)
if IOFD_ISSET(fd, addr rset) != 0:
if Event.User in pkey.events:
var data: uint64 = 0
if recv(fd, cast[pointer](addr(data)),
sizeof(uint64).cint, 0) != sizeof(uint64):
let err = osLastError()
if cint(err) != EAGAIN:
raiseOSError(err)
else:
inc(i)
inc(k)
continue
else:
flag = true
pkey.key.events = {Event.User}
else:
flag = true
pkey.key.events = {Event.Read}
if IOFD_ISSET(fd, addr wset) != 0:
pkey.key.events.incl(Event.Write)
if IOFD_ISSET(fd, addr eset) != 0:
pkey.key.events.incl(Event.Error)
flag = true
if flag:
results[rindex] = pkey.key
inc(rindex)
inc(k)
inc(i)
result = rindex
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey[T]] =
result = newSeq[ReadyKey[T]](FD_SETSIZE)
var count = selectInto(s, timeout, result)
result.setLen(count)
proc flush*[T](s: Selector[T]) = discard
template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0)
when hasThreadSupport:
template withSelectLock[T](s: Selector[T], body: untyped) =
acquire(s.lock)
{.locks: [s.lock].}:
try:
body
finally:
release(s.lock)
else:
template withSelectLock[T](s: Selector[T], body: untyped) =
body
template withData*[T](s: Selector[T], fd: SocketHandle, value,
body: untyped) =
mixin withSelectLock
s.withSelectLock():
var value: ptr T
let fdi = int(fd)
var i = 0
while i < FD_SETSIZE:
if s.fds[i].ident == fdi:
value = addr(s.fds[i].key.data)
break
inc(i)
if i != FD_SETSIZE:
body
template withData*[T](s: Selector[T], fd: SocketHandle, value,
body1, body2: untyped) =
mixin withSelectLock
s.withSelectLock():
var value: ptr T
let fdi = int(fd)
var i = 0
while i < FD_SETSIZE:
if s.fds[i].ident == fdi:
value = addr(s.fds[i].key.data)
break
inc(i)
if i != FD_SETSIZE:
body1
else:
body2

View file

@ -712,6 +712,7 @@ proc `%`*(b: bool): JsonNode =
proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode = proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
## Generic constructor for JSON data. Creates a new `JObject JsonNode` ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
if keyvals.len == 0: return newJArray()
result = newJObject() result = newJObject()
for key, val in items(keyVals): result.fields[key] = val for key, val in items(keyVals): result.fields[key] = val

View file

@ -66,13 +66,13 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
if err != 0'i32: if err != 0'i32:
when useWinUnicode: when useWinUnicode:
var msgbuf: WideCString var msgbuf: WideCString
if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf result = $msgbuf
if msgbuf != nil: localFree(cast[pointer](msgbuf)) if msgbuf != nil: localFree(cast[pointer](msgbuf))
else: else:
var msgbuf: cstring var msgbuf: cstring
if formatMessageA(0x00000100 or 0x00001000 or 0x00000200, if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf result = $msgbuf
if msgbuf != nil: localFree(msgbuf) if msgbuf != nil: localFree(msgbuf)

View file

@ -10,7 +10,7 @@
# Included by the ``os`` module but a module in its own right for NimScript # Included by the ``os`` module but a module in its own right for NimScript
# support. # support.
when isMainModule: when not declared(os):
{.pragma: rtl.} {.pragma: rtl.}
import strutils import strutils

View file

@ -142,6 +142,9 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} =
template charData*(my: XmlParser): string = template charData*(my: XmlParser): string =
## returns the character data for the events: ``xmlCharData``, ## returns the character data for the events: ``xmlCharData``,
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
## Raises an assertion in debug mode if ``my.kind`` is not one
## of those events. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
xmlSpecial}) xmlSpecial})
my.a my.a
@ -149,31 +152,49 @@ template charData*(my: XmlParser): string =
template elementName*(my: XmlParser): string = template elementName*(my: XmlParser): string =
## returns the element name for the events: ``xmlElementStart``, ## returns the element name for the events: ``xmlElementStart``,
## ``xmlElementEnd``, ``xmlElementOpen`` ## ``xmlElementEnd``, ``xmlElementOpen``
## Raises an assertion in debug mode if ``my.kind`` is not one
## of those events. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen}) assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen})
my.a my.a
template entityName*(my: XmlParser): string = template entityName*(my: XmlParser): string =
## returns the entity name for the event: ``xmlEntity`` ## returns the entity name for the event: ``xmlEntity``
## Raises an assertion in debug mode if ``my.kind`` is not
## ``xmlEntity``. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind == xmlEntity) assert(my.kind == xmlEntity)
my.a my.a
template attrKey*(my: XmlParser): string = template attrKey*(my: XmlParser): string =
## returns the attribute key for the event ``xmlAttribute`` ## returns the attribute key for the event ``xmlAttribute``
## Raises an assertion in debug mode if ``my.kind`` is not
## ``xmlAttribute``. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind == xmlAttribute) assert(my.kind == xmlAttribute)
my.a my.a
template attrValue*(my: XmlParser): string = template attrValue*(my: XmlParser): string =
## returns the attribute value for the event ``xmlAttribute`` ## returns the attribute value for the event ``xmlAttribute``
## Raises an assertion in debug mode if ``my.kind`` is not
## ``xmlAttribute``. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind == xmlAttribute) assert(my.kind == xmlAttribute)
my.b my.b
template piName*(my: XmlParser): string = template piName*(my: XmlParser): string =
## returns the processing instruction name for the event ``xmlPI`` ## returns the processing instruction name for the event ``xmlPI``
## Raises an assertion in debug mode if ``my.kind`` is not
## ``xmlPI``. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind == xmlPI) assert(my.kind == xmlPI)
my.a my.a
template piRest*(my: XmlParser): string = template piRest*(my: XmlParser): string =
## returns the rest of the processing instruction for the event ``xmlPI`` ## returns the rest of the processing instruction for the event ``xmlPI``
## Raises an assertion in debug mode if ``my.kind`` is not
## ``xmlPI``. In release mode, this will not trigger an error
## but the value returned will not be valid.
assert(my.kind == xmlPI) assert(my.kind == xmlPI)
my.b my.b

View file

@ -1841,8 +1841,8 @@ when isMainModule:
result.add ", " result.add ", "
result.add case n: result.add case n:
of 2: c[0].toLower & ": '" & c[1] & "'" of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'"
of 1: c[0].toLower & ": ''" of 1: toLowerAscii(c[0]) & ": ''"
else: "" else: ""
assert("Var1=key1;var2=Key2; VAR3". assert("Var1=key1;var2=Key2; VAR3".

View file

@ -26,6 +26,12 @@ include "system/inclrtl"
{.pop.} {.pop.}
# Support old split with set[char]
when defined(nimOldSplit):
{.pragma: deprecatedSplit, deprecated.}
else:
{.pragma: deprecatedSplit.}
type type
CharSet* {.deprecated.} = set[char] # for compatibility with Nim CharSet* {.deprecated.} = set[char] # for compatibility with Nim
{.deprecated: [TCharSet: CharSet].} {.deprecated: [TCharSet: CharSet].}
@ -64,8 +70,8 @@ const
## doAssert "01234".find(invalid) == -1 ## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2 ## doAssert "01A34".find(invalid) == 2
proc isAlpha*(c: char): bool {.noSideEffect, procvar, proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaChar".}= rtl, extern: "nsuIsAlphaAsciiChar".}=
## Checks whether or not `c` is alphabetical. ## Checks whether or not `c` is alphabetical.
## ##
## This checks a-z, A-Z ASCII characters only. ## This checks a-z, A-Z ASCII characters only.
@ -85,27 +91,27 @@ proc isDigit*(c: char): bool {.noSideEffect, procvar,
## This checks 0-9 ASCII characters only. ## This checks 0-9 ASCII characters only.
return c in Digits return c in Digits
proc isSpace*(c: char): bool {.noSideEffect, procvar, proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsSpaceChar".}= rtl, extern: "nsuIsSpaceAsciiChar".}=
## Checks whether or not `c` is a whitespace character. ## Checks whether or not `c` is a whitespace character.
return c in Whitespace return c in Whitespace
proc isLower*(c: char): bool {.noSideEffect, procvar, proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsLowerChar".}= rtl, extern: "nsuIsLowerAsciiChar".}=
## Checks whether or not `c` is a lower case character. ## Checks whether or not `c` is a lower case character.
## ##
## This checks ASCII characters only. ## This checks ASCII characters only.
return c in {'a'..'z'} return c in {'a'..'z'}
proc isUpper*(c: char): bool {.noSideEffect, procvar, proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsUpperChar".}= rtl, extern: "nsuIsUpperAsciiChar".}=
## Checks whether or not `c` is an upper case character. ## Checks whether or not `c` is an upper case character.
## ##
## This checks ASCII characters only. ## This checks ASCII characters only.
return c in {'A'..'Z'} return c in {'A'..'Z'}
proc isAlpha*(s: string): bool {.noSideEffect, procvar, proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaStr".}= rtl, extern: "nsuIsAlphaAsciiStr".}=
## Checks whether or not `s` is alphabetical. ## Checks whether or not `s` is alphabetical.
## ##
## This checks a-z, A-Z ASCII characters only. ## This checks a-z, A-Z ASCII characters only.
@ -117,7 +123,7 @@ proc isAlpha*(s: string): bool {.noSideEffect, procvar,
result = true result = true
for c in s: for c in s:
result = c.isAlpha() and result result = c.isAlphaAscii() and result
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaNumericStr".}= rtl, extern: "nsuIsAlphaNumericStr".}=
@ -149,8 +155,8 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
for c in s: for c in s:
result = c.isDigit() and result result = c.isDigit() and result
proc isSpace*(s: string): bool {.noSideEffect, procvar, proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsSpaceStr".}= rtl, extern: "nsuIsSpaceAsciiStr".}=
## Checks whether or not `s` is completely whitespace. ## Checks whether or not `s` is completely whitespace.
## ##
## Returns true if all characters in `s` are whitespace ## Returns true if all characters in `s` are whitespace
@ -160,11 +166,11 @@ proc isSpace*(s: string): bool {.noSideEffect, procvar,
result = true result = true
for c in s: for c in s:
if not c.isSpace(): if not c.isSpaceAscii():
return false return false
proc isLower*(s: string): bool {.noSideEffect, procvar, proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsLowerStr".}= rtl, extern: "nsuIsLowerAsciiStr".}=
## Checks whether or not `s` contains all lower case characters. ## Checks whether or not `s` contains all lower case characters.
## ##
## This checks ASCII characters only. ## This checks ASCII characters only.
@ -175,10 +181,10 @@ proc isLower*(s: string): bool {.noSideEffect, procvar,
result = true result = true
for c in s: for c in s:
result = c.isLower() and result result = c.isLowerAscii() and result
proc isUpper*(s: string): bool {.noSideEffect, procvar, proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsUpperStr".}= rtl, extern: "nsuIsUpperAsciiStr".}=
## Checks whether or not `s` contains all upper case characters. ## Checks whether or not `s` contains all upper case characters.
## ##
## This checks ASCII characters only. ## This checks ASCII characters only.
@ -189,10 +195,10 @@ proc isUpper*(s: string): bool {.noSideEffect, procvar,
result = true result = true
for c in s: for c in s:
result = c.isUpper() and result result = c.isUpperAscii() and result
proc toLower*(c: char): char {.noSideEffect, procvar, proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} = rtl, extern: "nsuToLowerAsciiChar".} =
## Converts `c` into lower case. ## Converts `c` into lower case.
## ##
## This works only for the letters ``A-Z``. See `unicode.toLower ## This works only for the letters ``A-Z``. See `unicode.toLower
@ -203,8 +209,8 @@ proc toLower*(c: char): char {.noSideEffect, procvar,
else: else:
result = c result = c
proc toLower*(s: string): string {.noSideEffect, procvar, proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuToLowerStr".} = rtl, extern: "nsuToLowerAsciiStr".} =
## Converts `s` into lower case. ## Converts `s` into lower case.
## ##
## This works only for the letters ``A-Z``. See `unicode.toLower ## This works only for the letters ``A-Z``. See `unicode.toLower
@ -212,10 +218,10 @@ proc toLower*(s: string): string {.noSideEffect, procvar,
## character. ## character.
result = newString(len(s)) result = newString(len(s))
for i in 0..len(s) - 1: for i in 0..len(s) - 1:
result[i] = toLower(s[i]) result[i] = toLowerAscii(s[i])
proc toUpper*(c: char): char {.noSideEffect, procvar, proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToUpperChar".} = rtl, extern: "nsuToUpperAsciiChar".} =
## Converts `c` into upper case. ## Converts `c` into upper case.
## ##
## This works only for the letters ``A-Z``. See `unicode.toUpper ## This works only for the letters ``A-Z``. See `unicode.toUpper
@ -226,8 +232,8 @@ proc toUpper*(c: char): char {.noSideEffect, procvar,
else: else:
result = c result = c
proc toUpper*(s: string): string {.noSideEffect, procvar, proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuToUpperStr".} = rtl, extern: "nsuToUpperAsciiStr".} =
## Converts `s` into upper case. ## Converts `s` into upper case.
## ##
## This works only for the letters ``A-Z``. See `unicode.toUpper ## This works only for the letters ``A-Z``. See `unicode.toUpper
@ -235,14 +241,145 @@ proc toUpper*(s: string): string {.noSideEffect, procvar,
## character. ## character.
result = newString(len(s)) result = newString(len(s))
for i in 0..len(s) - 1: for i in 0..len(s) - 1:
result[i] = toUpper(s[i]) result[i] = toUpperAscii(s[i])
proc capitalize*(s: string): string {.noSideEffect, procvar, proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuCapitalize".} = rtl, extern: "nsuCapitalizeAscii".} =
## Converts the first character of `s` into upper case. ## Converts the first character of `s` into upper case.
## ##
## This works only for the letters ``A-Z``. ## This works only for the letters ``A-Z``.
result = toUpper(s[0]) & substr(s, 1) result = toUpperAscii(s[0]) & substr(s, 1)
proc isSpace*(c: char): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsSpaceChar".}=
## Checks whether or not `c` is a whitespace character.
##
## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
isSpaceAscii(c)
proc isLower*(c: char): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsLowerChar".}=
## Checks whether or not `c` is a lower case character.
##
## This checks ASCII characters only.
##
## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
isLowerAscii(c)
proc isUpper*(c: char): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsUpperChar".}=
## Checks whether or not `c` is an upper case character.
##
## This checks ASCII characters only.
##
## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
isUpperAscii(c)
proc isAlpha*(c: char): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsAlphaChar".}=
## Checks whether or not `c` is alphabetical.
##
## This checks a-z, A-Z ASCII characters only.
##
## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
isAlphaAscii(c)
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsAlphaStr".}=
## Checks whether or not `s` is alphabetical.
##
## This checks a-z, A-Z ASCII characters only.
## Returns true if all characters in `s` are
## alphabetic and there is at least one character
## in `s`.
##
## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
isAlphaAscii(s)
proc isSpace*(s: string): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsSpaceStr".}=
## Checks whether or not `s` is completely whitespace.
##
## Returns true if all characters in `s` are whitespace
## characters and there is at least one character in `s`.
##
## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
isSpaceAscii(s)
proc isLower*(s: string): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsLowerStr".}=
## Checks whether or not `s` contains all lower case characters.
##
## This checks ASCII characters only.
## Returns true if all characters in `s` are lower case
## and there is at least one character in `s`.
##
## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
isLowerAscii(s)
proc isUpper*(s: string): bool {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuIsUpperStr".}=
## Checks whether or not `s` contains all upper case characters.
##
## This checks ASCII characters only.
## Returns true if all characters in `s` are upper case
## and there is at least one character in `s`.
##
## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
isUpperAscii(s)
proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuToLowerChar".} =
## Converts `c` into lower case.
##
## This works only for the letters ``A-Z``. See `unicode.toLower
## <unicode.html#toLower>`_ for a version that works for any Unicode
## character.
##
## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
toLowerAscii(c)
proc toLower*(s: string): string {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuToLowerStr".} =
## Converts `s` into lower case.
##
## This works only for the letters ``A-Z``. See `unicode.toLower
## <unicode.html#toLower>`_ for a version that works for any Unicode
## character.
##
## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
toLowerAscii(s)
proc toUpper*(c: char): char {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuToUpperChar".} =
## Converts `c` into upper case.
##
## This works only for the letters ``A-Z``. See `unicode.toUpper
## <unicode.html#toUpper>`_ for a version that works for any Unicode
## character.
##
## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
toUpperAscii(c)
proc toUpper*(s: string): string {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuToUpperStr".} =
## Converts `s` into upper case.
##
## This works only for the letters ``A-Z``. See `unicode.toUpper
## <unicode.html#toUpper>`_ for a version that works for any Unicode
## character.
##
## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
toUpperAscii(s)
proc capitalize*(s: string): string {.noSideEffect, procvar,
rtl, deprecated, extern: "nsuCapitalize".} =
## Converts the first character of `s` into upper case.
##
## This works only for the letters ``A-Z``.
##
## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead.
capitalizeAscii(s)
proc normalize*(s: string): string {.noSideEffect, procvar, proc normalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuNormalize".} = rtl, extern: "nsuNormalize".} =
@ -271,7 +408,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
var i = 0 var i = 0
var m = min(a.len, b.len) var m = min(a.len, b.len)
while i < m: while i < m:
result = ord(toLower(a[i])) - ord(toLower(b[i])) result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
if result != 0: return if result != 0: return
inc(i) inc(i)
result = a.len - b.len result = a.len - b.len
@ -292,8 +429,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
while true: while true:
while a[i] == '_': inc(i) while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i]) var aa = toLowerAscii(a[i])
var bb = toLower(b[j]) var bb = toLowerAscii(b[j])
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break if result != 0 or aa == '\0': break
inc(i) inc(i)
@ -341,17 +478,63 @@ proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "
if not c.isSpace(): if not c.isSpace():
return false return false
proc substrEq(s: string, pos: int, substr: string): bool =
var i = 0
var length = substr.len
while i < length and s[pos+i] == substr[i]:
inc i
return i == length
# --------- Private templates for different split separators -----------
template stringHasSep(s: string, index: int, seps: set[char]): bool =
s[index] in seps
template stringHasSep(s: string, index: int, sep: char): bool =
s[index] == sep
template stringHasSep(s: string, index: int, sep: string): bool =
s.substrEq(index, sep)
template splitCommon(s, sep, maxsplit, sepLen) =
## Common code for split procedures
var last = 0
var splits = maxsplit
if len(s) > 0:
while last <= len(s):
var first = last
while last < len(s) and not stringHasSep(s, last, sep):
inc(last)
if splits == 0: last = len(s)
yield substr(s, first, last-1)
if splits == 0: break
dec(splits)
inc(last, sepLen)
template oldSplit(s, seps, maxsplit) =
var last = 0
var splits = maxsplit
assert(not ('\0' in seps))
while last < len(s):
while s[last] in seps: inc(last)
var first = last
while last < len(s) and s[last] notin seps: inc(last)
if first <= last-1:
if splits == 0: last = len(s)
yield substr(s, first, last-1)
if splits == 0: break
dec(splits)
iterator split*(s: string, seps: set[char] = Whitespace, iterator split*(s: string, seps: set[char] = Whitespace,
maxsplit: int = -1): string = maxsplit: int = -1): string =
## Splits the string `s` into substrings using a group of separators. ## Splits the string `s` into substrings using a group of separators.
## ##
## Substrings are separated by a substring containing only `seps`. Note ## Substrings are separated by a substring containing only `seps`.
## that whole sequences of characters found in ``seps`` will be counted as
## a single split point and leading/trailing separators will be ignored.
## The following example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## for word in split(" this is an example "): ## for word in split("this\lis an\texample"):
## writeLine(stdout, word) ## writeLine(stdout, word)
## ##
## ...generates this output: ## ...generates this output:
@ -365,7 +548,7 @@ iterator split*(s: string, seps: set[char] = Whitespace,
## And the following code: ## And the following code:
## ##
## .. code-block:: nim ## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", {';'}): ## for word in split("this:is;an$example", {';', ':', '$'}):
## writeLine(stdout, word) ## writeLine(stdout, word)
## ##
## ...produces the same output as the first example. The code: ## ...produces the same output as the first example. The code:
@ -386,26 +569,26 @@ iterator split*(s: string, seps: set[char] = Whitespace,
## "08" ## "08"
## "08.398990" ## "08.398990"
## ##
var last = 0 when defined(nimOldSplit):
var splits = maxsplit oldSplit(s, seps, maxsplit)
assert(not ('\0' in seps)) else:
while last < len(s): splitCommon(s, seps, maxsplit, 1)
while s[last] in seps: inc(last)
var first = last iterator splitWhitespace*(s: string): string =
while last < len(s) and s[last] notin seps: inc(last) # BUGFIX! ## Splits at whitespace.
if first <= last-1: oldSplit(s, Whitespace, -1)
if splits == 0: last = len(s)
yield substr(s, first, last-1) proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
if splits == 0: break rtl, extern: "nsuSplitWhitespace".} =
dec(splits) ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
## iterator, but is a proc that returns a sequence of substrings.
accumulateResult(splitWhitespace(s))
iterator split*(s: string, sep: char, maxsplit: int = -1): string = iterator split*(s: string, sep: char, maxsplit: int = -1): string =
## Splits the string `s` into substrings using a single separator. ## Splits the string `s` into substrings using a single separator.
## ##
## Substrings are separated by the character `sep`. ## Substrings are separated by the character `sep`.
## Unlike the version of the iterator which accepts a set of separator ## The code:
## characters, this proc will not coalesce groups of the
## separator, returning a string for each found character. The code:
## ##
## .. code-block:: nim ## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", ';'): ## for word in split(";;this;is;an;;example;;;", ';'):
@ -425,56 +608,27 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string =
## "" ## ""
## "" ## ""
## ##
var last = 0 splitCommon(s, sep, maxsplit, 1)
var splits = maxsplit
assert('\0' != sep)
if len(s) > 0:
# `<=` is correct here for the edge cases!
while last <= len(s):
var first = last
while last < len(s) and s[last] != sep: inc(last)
if splits == 0: last = len(s)
yield substr(s, first, last-1)
if splits == 0: break
dec(splits)
inc(last)
proc substrEq(s: string, pos: int, substr: string): bool =
var i = 0
var length = substr.len
while i < length and s[pos+i] == substr[i]:
inc i
return i == length
iterator split*(s: string, sep: string, maxsplit: int = -1): string = iterator split*(s: string, sep: string, maxsplit: int = -1): string =
## Splits the string `s` into substrings using a string separator. ## Splits the string `s` into substrings using a string separator.
## ##
## Substrings are separated by the string `sep`. ## Substrings are separated by the string `sep`.
var last = 0 ## The code:
var splits = maxsplit ##
## .. code-block:: nim
## for word in split("thisDATAisDATAcorrupted", "DATA"):
## writeLine(stdout, word)
##
## Results in:
##
## .. code-block::
## "this"
## "is"
## "corrupted"
##
if len(s) > 0: splitCommon(s, sep, maxsplit, sep.len)
while last <= len(s):
var first = last
while last < len(s) and not s.substrEq(last, sep):
inc(last)
if splits == 0: last = len(s)
yield substr(s, first, last-1)
if splits == 0: break
dec(splits)
inc(last, sep.len)
# --------- Private templates for different rsplit separators -----------
template stringHasSep(s: string, index: int, seps: set[char]): bool =
s[index] in seps
template stringHasSep(s: string, index: int, sep: char): bool =
s[index] == sep
template stringHasSep(s: string, index: int, sep: string): bool =
s.substrEq(index, sep)
template rsplitCommon(s, sep, maxsplit, sepLen) = template rsplitCommon(s, sep, maxsplit, sepLen) =
## Common code for rsplit functions ## Common code for rsplit functions
@ -2138,13 +2292,13 @@ when isMainModule:
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
doAssert isAlpha('r') doAssert isAlphaAscii('r')
doAssert isAlpha('A') doAssert isAlphaAscii('A')
doAssert(not isAlpha('$')) doAssert(not isAlphaAscii('$'))
doAssert isAlpha("Rasp") doAssert isAlphaAscii("Rasp")
doAssert isAlpha("Args") doAssert isAlphaAscii("Args")
doAssert(not isAlpha("$Tomato")) doAssert(not isAlphaAscii("$Tomato"))
doAssert isAlphaNumeric('3') doAssert isAlphaNumeric('3')
doAssert isAlphaNumeric('R') doAssert isAlphaNumeric('R')
@ -2163,13 +2317,13 @@ when isMainModule:
doAssert(not isDigit("12.33")) doAssert(not isDigit("12.33"))
doAssert(not isDigit("A45b")) doAssert(not isDigit("A45b"))
doAssert isSpace('\t') doAssert isSpaceAscii('\t')
doAssert isSpace('\l') doAssert isSpaceAscii('\l')
doAssert(not isSpace('A')) doAssert(not isSpaceAscii('A'))
doAssert isSpace("\t\l \v\r\f") doAssert isSpaceAscii("\t\l \v\r\f")
doAssert isSpace(" ") doAssert isSpaceAscii(" ")
doAssert(not isSpace("ABc \td")) doAssert(not isSpaceAscii("ABc \td"))
doAssert(isNilOrEmpty("")) doAssert(isNilOrEmpty(""))
doAssert(isNilOrEmpty(nil)) doAssert(isNilOrEmpty(nil))
@ -2182,24 +2336,24 @@ when isMainModule:
doAssert(isNilOrWhitespace("\t\l \v\r\f")) doAssert(isNilOrWhitespace("\t\l \v\r\f"))
doAssert(not isNilOrWhitespace("ABc \td")) doAssert(not isNilOrWhitespace("ABc \td"))
doAssert isLower('a') doAssert isLowerAscii('a')
doAssert isLower('z') doAssert isLowerAscii('z')
doAssert(not isLower('A')) doAssert(not isLowerAscii('A'))
doAssert(not isLower('5')) doAssert(not isLowerAscii('5'))
doAssert(not isLower('&')) doAssert(not isLowerAscii('&'))
doAssert isLower("abcd") doAssert isLowerAscii("abcd")
doAssert(not isLower("abCD")) doAssert(not isLowerAscii("abCD"))
doAssert(not isLower("33aa")) doAssert(not isLowerAscii("33aa"))
doAssert isUpper('A') doAssert isUpperAscii('A')
doAssert(not isUpper('b')) doAssert(not isUpperAscii('b'))
doAssert(not isUpper('5')) doAssert(not isUpperAscii('5'))
doAssert(not isUpper('%')) doAssert(not isUpperAscii('%'))
doAssert isUpper("ABC") doAssert isUpperAscii("ABC")
doAssert(not isUpper("AAcc")) doAssert(not isUpperAscii("AAcc"))
doAssert(not isUpper("A#$")) doAssert(not isUpperAscii("A#$"))
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"] doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"] doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
@ -2244,11 +2398,14 @@ bar
bar bar
""".unindent() == "foo\nfoo\nbar\n" """.unindent() == "foo\nfoo\nbar\n"
let s = " this is an example " let s = " this is an example "
doAssert s.split() == @["this", "is", "an", "example"] let s2 = ":this;is;an:example;;"
doAssert s.split(maxsplit=4) == @["this", "is", "an", "example"]
doAssert s.split(' ', maxsplit=4) == @["", "this", "", "", "is an example "] doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
doAssert s.split(" ", maxsplit=4) == @["", "this", "", "", "is an example "] doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
block: # formatEng tests block: # formatEng tests
doAssert formatEng(0, 2, trim=false) == "0.00" doAssert formatEng(0, 2, trim=false) == "0.00"

View file

@ -1369,6 +1369,64 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
(c >= 0x20d0 and c <= 0x20ff) or (c >= 0x20d0 and c <= 0x20ff) or
(c >= 0xfe20 and c <= 0xfe2f)) (c >= 0xfe20 and c <= 0xfe2f))
template runeCheck(s, runeProc) =
## Common code for rune.isLower, rune.isUpper, etc
result = if len(s) == 0: false else: true
var
i = 0
rune: Rune
while i < len(s) and result:
fastRuneAt(s, i, rune, doInc=true)
result = runeProc(rune) and result
proc isUpper*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Returns true iff `s` contains all upper case unicode characters.
runeCheck(s, isUpper)
proc isLower*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Returns true iff `s` contains all lower case unicode characters.
runeCheck(s, isLower)
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Returns true iff `s` contains all alphabetic unicode characters.
runeCheck(s, isAlpha)
proc isSpace*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Returns true iff `s` contains all whitespace unicode characters.
runeCheck(s, isWhiteSpace)
template convertRune(s, runeProc) =
## Convert runes in `s` using `runeProc` as the converter.
result = newString(len(s))
var
i = 0
lastIndex = 0
rune: Rune
while i < len(s):
lastIndex = i
fastRuneAt(s, i, rune, doInc=true)
rune = runeProc(rune)
rune.fastToUTF8Copy(result, lastIndex)
proc toUpper*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Converts `s` into upper-case unicode characters.
convertRune(s, toUpper)
proc toLower*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nuc$1Str".} =
## Converts `s` into lower-case unicode characters.
convertRune(s, toLower)
proc swapCase*(s: string): string {.noSideEffect, procvar, proc swapCase*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nuc$1".} = rtl, extern: "nuc$1".} =
## Swaps the case of unicode characters in `s` ## Swaps the case of unicode characters in `s`
@ -1395,6 +1453,20 @@ proc swapCase*(s: string): string {.noSideEffect, procvar,
rune.fastToUTF8Copy(result, lastIndex) rune.fastToUTF8Copy(result, lastIndex)
proc capitalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nuc$1".} =
## Converts the first character of `s` into an upper-case unicode character.
if len(s) == 0:
return s
var
rune: Rune
i = 0
fastRuneAt(s, i, rune, doInc=true)
result = $toUpper(rune) & substr(s, i)
proc translate*(s: string, replacements: proc(key: string): string): string {. proc translate*(s: string, replacements: proc(key: string): string): string {.
rtl, extern: "nuc$1".} = rtl, extern: "nuc$1".} =
## Translates words in a string using the `replacements` proc to substitute ## Translates words in a string using the `replacements` proc to substitute
@ -1618,6 +1690,10 @@ when isMainModule:
doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma" doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
doAssert title("") == "" doAssert title("") == ""
doAssert capitalize("βeta") == "Βeta"
doAssert capitalize("foo") == "Foo"
doAssert capitalize("") == ""
doAssert isTitle("Foo") doAssert isTitle("Foo")
doAssert(not isTitle("Foo bar")) doAssert(not isTitle("Foo bar"))
doAssert(not isTitle("αlpha Βeta")) doAssert(not isTitle("αlpha Βeta"))
@ -1630,6 +1706,64 @@ when isMainModule:
doAssert swapCase("a✓B") == "A✓b" doAssert swapCase("a✓B") == "A✓b"
doAssert swapCase("") == "" doAssert swapCase("") == ""
doAssert isAlpha("r")
doAssert isAlpha("α")
doAssert(not isAlpha("$"))
doAssert(not isAlpha(""))
doAssert isAlpha("Βeta")
doAssert isAlpha("Args")
doAssert(not isAlpha("$Foo✓"))
doAssert isSpace("\t")
doAssert isSpace("\l")
doAssert(not isSpace("Β"))
doAssert(not isSpace("Βeta"))
doAssert isSpace("\t\l \v\r\f")
doAssert isSpace(" ")
doAssert(not isSpace(""))
doAssert(not isSpace("ΑΓc \td"))
doAssert isLower("a")
doAssert isLower("γ")
doAssert(not isLower("Γ"))
doAssert(not isLower("4"))
doAssert(not isLower(""))
doAssert isLower("abcdγ")
doAssert(not isLower("abCDΓ"))
doAssert(not isLower("33aaΓ"))
doAssert isUpper("Γ")
doAssert(not isUpper("b"))
doAssert(not isUpper("α"))
doAssert(not isUpper("✓"))
doAssert(not isUpper(""))
doAssert isUpper("ΑΒΓ")
doAssert(not isUpper("AAccβ"))
doAssert(not isUpper("A#$β"))
doAssert toUpper("Γ") == "Γ"
doAssert toUpper("b") == "B"
doAssert toUpper("α") == "Α"
doAssert toUpper("✓") == "✓"
doAssert toUpper("") == ""
doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
doAssert toUpper("AAccβ") == "AACCΒ"
doAssert toUpper("A✓$β") == "A✓$Β"
doAssert toLower("a") == "a"
doAssert toLower("γ") == "γ"
doAssert toLower("Γ") == "γ"
doAssert toLower("4") == "4"
doAssert toLower("") == ""
doAssert toLower("abcdγ") == "abcdγ"
doAssert toLower("abCDΓ") == "abcdγ"
doAssert toLower("33aaΓ") == "33aaγ"
doAssert reversed("Reverse this!") == "!siht esreveR" doAssert reversed("Reverse this!") == "!siht esreveR"
doAssert reversed("先秦兩漢") == "漢兩秦先" doAssert reversed("先秦兩漢") == "漢兩秦先"

View file

@ -3633,6 +3633,27 @@ proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
elif x.isNil or y.isNil: result = false elif x.isNil or y.isNil: result = false
else: result = strcmp(x, y) == 0 else: result = strcmp(x, y) == 0
template closureScope*(body: untyped): stmt =
## Useful when creating a closure in a loop to capture local loop variables by
## their current iteration values. Example:
##
## .. code-block:: nim
## var myClosure : proc()
## # without closureScope:
## for i in 0 .. 5:
## let j = i
## if j == 3:
## myClosure = proc() = echo j
## myClosure() # outputs 5. `j` is changed after closure creation
## # with closureScope:
## for i in 0 .. 5:
## closureScope: # Everything in this scope is locked after closure creation
## let j = i
## if j == 3:
## myClosure = proc() = echo j
## myClosure() # outputs 3
(proc() = body)()
{.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.} {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
when defined(nimconfig): when defined(nimconfig):

View file

@ -124,7 +124,9 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
copyMem(dest, src, mt.size) copyMem(dest, src, mt.size)
proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} = proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
GC_disable()
genericDeepCopyAux(dest, src, mt) genericDeepCopyAux(dest, src, mt)
GC_enable()
proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} = proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
# also invoked for 'string' # also invoked for 'string'

View file

@ -39,6 +39,9 @@ proc getCurrentDir(): string = builtin
proc rawExec(cmd: string): int {.tags: [ExecIOEffect], raises: [OSError].} = proc rawExec(cmd: string): int {.tags: [ExecIOEffect], raises: [OSError].} =
builtin builtin
proc warningImpl(arg, orig: string) = discard
proc hintImpl(arg, orig: string) = discard
proc paramStr*(i: int): string = proc paramStr*(i: int): string =
## Retrieves the ``i``'th command line parameter. ## Retrieves the ``i``'th command line parameter.
builtin builtin
@ -52,6 +55,16 @@ proc switch*(key: string, val="") =
## example ``switch("checks", "on")``. ## example ``switch("checks", "on")``.
builtin builtin
proc warning*(name: string; val: bool) =
## Disables or enables a specific warning.
let v = if val: "on" else: "off"
warningImpl(name & "]:" & v, "warning[" & name & "]:" & v)
proc hint*(name: string; val: bool) =
## Disables or enables a specific hint.
let v = if val: "on" else: "off"
hintImpl(name & "]:" & v, "hint[" & name & "]:" & v)
proc getCommand*(): string = proc getCommand*(): string =
## Gets the Nim command that the compiler has been invoked with, for example ## Gets the Nim command that the compiler has been invoked with, for example
## "c", "js", "build", "help". ## "c", "js", "build", "help".

View file

@ -150,8 +150,9 @@ elif defined(windows):
#VirtualFree(p, size, MEM_DECOMMIT) #VirtualFree(p, size, MEM_DECOMMIT)
elif hostOS == "standalone": elif hostOS == "standalone":
const StandaloneHeapSize {.intdefine.}: int = 1024 * PageSize
var var
theHeap: array[1024*PageSize, float64] # 'float64' for alignment theHeap: array[StandaloneHeapSize, float64] # 'float64' for alignment
bumpPointer = cast[int](addr theHeap) bumpPointer = cast[int](addr theHeap)
proc osAllocPages(size: int): pointer {.inline.} = proc osAllocPages(size: int): pointer {.inline.} =

View file

@ -73,23 +73,20 @@ proc reprChar(x: char): string {.compilerRtl.} =
add result, "\'" add result, "\'"
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
# we read an 'int' but this may have been too large, so mask the other bits: ## Return string representation for enumeration values
let b = (sizeof(int)-typ.size)*8 # bits var n = typ.node
let m = 1 shl (b-1) # mask
var o = e and ((1 shl b)-1) # clear upper bits
o = (o xor m) - m # sign extend
# XXX we need a proper narrowing based on signedness here
#e and ((1 shl (typ.size*8)) - 1)
if ntfEnumHole notin typ.flags: if ntfEnumHole notin typ.flags:
if o <% typ.node.len: let o = e - n.sons[0].offset
return $typ.node.sons[o].name if o >= 0 and o <% typ.node.len:
return $n.sons[o].name
else: else:
# ugh we need a slow linear search: # ugh we need a slow linear search:
var n = typ.node
var s = n.sons var s = n.sons
for i in 0 .. n.len-1: for i in 0 .. n.len-1:
if s[i].offset == o: return $s[i].name if s[i].offset == e:
result = $o & " (invalid data!)" return $s[i].name
result = $e & " (invalid data!)"
type type
PByteArray = ptr array[0.. 0xffff, int8] PByteArray = ptr array[0.. 0xffff, int8]

File diff suppressed because it is too large Load diff

View file

@ -1 +1,4 @@
discard """
output: ""
"""
import "../template/utemplates", "../closure/uclosures" import "../template/utemplates", "../closure/uclosures"

View file

@ -0,0 +1,9 @@
import asyncdispatch
when true:
# bug #2377
proc test[T](v: T) {.async.} =
echo $v
asyncCheck test[int](1)

View file

@ -58,6 +58,7 @@ when not defined(windows):
discard posix.connect(client_socket, aiList.ai_addr, discard posix.connect(client_socket, aiList.ai_addr,
aiList.ai_addrlen.Socklen) aiList.ai_addrlen.Socklen)
dealloc(aiList) dealloc(aiList)
discard selector.select(100)
var rc1 = selector.select(100) var rc1 = selector.select(100)
assert(len(rc1) == 2) assert(len(rc1) == 2)
@ -124,7 +125,7 @@ when not defined(windows):
proc event_notification_test(): bool = proc event_notification_test(): bool =
var selector = newSelector[int]() var selector = newSelector[int]()
var event = newEvent() var event = newSelectEvent()
selector.registerEvent(event, 1) selector.registerEvent(event, 1)
selector.flush() selector.flush()
event.setEvent() event.setEvent()
@ -235,7 +236,7 @@ when not defined(windows):
thr: array [0..7, Thread[SelectEvent]] thr: array [0..7, Thread[SelectEvent]]
var selector = newSelector[int]() var selector = newSelector[int]()
var sock = newNativeSocket() var sock = newNativeSocket()
var event = newEvent() var event = newSelectEvent()
for i in 0..high(thr): for i in 0..high(thr):
createThread(thr[i], event_wait_thread, event) createThread(thr[i], event_wait_thread, event)
selector.registerHandle(sock, {Event.Read}, 1) selector.registerHandle(sock, {Event.Read}, 1)
@ -358,7 +359,7 @@ else:
proc event_notification_test(): bool = proc event_notification_test(): bool =
var selector = newSelector[int]() var selector = newSelector[int]()
var event = newEvent() var event = newSelectEvent()
selector.registerEvent(event, 1) selector.registerEvent(event, 1)
selector.flush() selector.flush()
event.setEvent() event.setEvent()
@ -391,7 +392,7 @@ else:
proc mt_event_test(): bool = proc mt_event_test(): bool =
var thr: array [0..7, Thread[SelectEvent]] var thr: array [0..7, Thread[SelectEvent]]
var event = newEvent() var event = newSelectEvent()
for i in 0..high(thr): for i in 0..high(thr):
createThread(thr[i], event_wait_thread, event) createThread(thr[i], event_wait_thread, event)
event.setEvent() event.setEvent()

View file

@ -0,0 +1,23 @@
# bug #4371
import strutils, asyncdispatch, asynchttpserver
type
List[A] = ref object
value: A
next: List[A]
StrPair* = tuple[k, v: string]
Context* = object
position*: int
accept*: bool
headers*: List[StrPair]
Handler* = proc(req: ref Request, ctx: Context): Future[Context]
proc logging*(handler: Handler): auto =
proc h(req: ref Request, ctx: Context): Future[Context] {.async.} =
let ret = handler(req, ctx)
debugEcho "$3 $1 $2".format(req.reqMethod, req.url.path, req.hostname)
return await ret
return h

View file

@ -1,12 +1,23 @@
# This test is included from within tunittests
import unittest import unittest
test "loop variables are captured by copy": test "loop variables are captured by ref":
var funcs: seq[proc (): int {.closure.}] = @[] var funcs: seq[proc (): int {.closure.}] = @[]
for i in 0..10: for i in 0..10:
let ii = i let ii = i
funcs.add do -> int: return ii * ii funcs.add do -> int: return ii * ii
check funcs[0]() == 100
check funcs[3]() == 100
test "loop variables in closureScope are captured by copy":
var funcs: seq[proc (): int {.closure.}] = @[]
for i in 0..10:
closureScope:
let ii = i
funcs.add do -> int: return ii * ii
check funcs[0]() == 0 check funcs[0]() == 0
check funcs[3]() == 9 check funcs[3]() == 9

View file

@ -0,0 +1,28 @@
discard """
output: '''b()
720 120.0'''
"""
# bug #3055
proc b(t: int | string)
proc a(t: int) = b(t)
proc b(t: int | string) = echo "b()"
a(1)
# test recursive generics still work:
proc fac[T](x: T): T =
if x == 0: return 1
else: return fac(x-1)*x
echo fac(6), " ", fac(5.0)
when false:
# This still doesn't work...
# test recursive generic with forwarding:
proc fac2[T](x: T): T
echo fac2(6), " ", fac2(5.0)
proc fac2[T](x: T): T =
if x == 0: return 1
else: return fac2(x-1)*x

View file

@ -0,0 +1,42 @@
discard """
output: '''3
OK
56
123
56
61'''
"""
import macros
# Bug from the forum
macro addEcho1(s: untyped): stmt =
s.body.add(newCall("echo", newStrLitNode("OK")))
result = s
proc f1() {.addEcho1.} =
let i = 1+2
echo i
f1()
# bug #537
proc test(): seq[NimNode] {.compiletime.} =
result = @[]
result.add parseExpr("echo 56")
result.add parseExpr("echo 123")
result.add parseExpr("echo 56")
proc foo(): seq[NimNode] {.compiletime.} =
result = @[]
result.add test()
result.add parseExpr("echo(5+56)")
macro bar(): stmt =
result = newNimNode(nnkStmtList)
let x = foo()
for xx in x:
result.add xx
echo treeRepr(result)
bar()

View file

@ -1,5 +1,5 @@
discard """ discard """
output: "empty" output: "void"
""" """
# bug #898 # bug #898

View file

@ -0,0 +1,21 @@
discard """
errormsg: "type mismatch: got (Future[system.int], void)"
line: 20
"""
type
Future[T] = object
value: T
proc complete[T](x: T) =
echo "completed"
let y = x
proc complete*[T](future: var Future[T], val: T) =
future.value = val
var a: Future[int]
complete(a):
echo "yielding void"

View file

@ -0,0 +1,9 @@
##.
import system except `+`
discard """
errormsg: "undeclared identifier: '+'"
line: 9
"""
# Testament requires that the initial """ occurs before the 40th byte
# in the file. No kidding...
echo 4+5

View file

@ -8,6 +8,9 @@ import ospaths
--forceBuild --forceBuild
warning("uninit", off)
hint("processing", off)
task listDirs, "lists every subdirectory": task listDirs, "lists every subdirectory":
for x in listDirs("."): for x in listDirs("."):
echo "DIR ", x echo "DIR ", x

View file

@ -9,7 +9,7 @@ for w in split("|abc|xy|z", {'|'}):
s.add("#") s.add("#")
s.add(w) s.add(w)
if s == "#abc#xy#z": if s == "##abc#xy#z":
echo "true" echo "true"
else: else:
echo "false" echo "false"

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 16 line: 16
errormsg: "type mismatch: got (empty)" errormsg: "type mismatch: got (void)"
""" """
# bug #950 # bug #950

16
tests/vm/tvmmisc.nim Normal file
View file

@ -0,0 +1,16 @@
# bug #4462
import macros
proc foo(t: typedesc) {.compileTime.} =
echo getType(t).treeRepr
static:
foo(int)
# #4412
proc default[T](t: typedesc[T]): T {.inline.} = discard
static:
var x = default(type(0))

View file

@ -3,7 +3,35 @@
# result = "#! /bin/sh\n# Generated by niminst\n" # result = "#! /bin/sh\n# Generated by niminst\n"
# var proj = c.name.toLower # var proj = c.name.toLower
set -e ## Current directory you start script from
BASE_DIR=$(pwd)
## The following one-liner takes directory path which contains install script.
## `command -v -- "$0"` takes path if script sourced from interactive shell
## `dirname` returns relative directory path to install script
## `cd -P` dive into directory to use `pwd`
## `pwd -P` prints full path to install script directory path
## -P option allows to use symlinks in path
## Good explanation can be found here:
## http://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
NIM_DIR=$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)
go_back() {
cd $BASE_DIR
}
## Go to base dir on exit
trap go_back EXIT
install_error() {
echo "Nim installation failed!"
exit 1
}
## Exit if any command failed
trap install_error ERR ## `set -e` alternative
cd $NIM_DIR
if [ $# -eq 1 ] ; then if [ $# -eq 1 ] ; then
# if c.cat[fcUnixBin].len > 0: # if c.cat[fcUnixBin].len > 0:

View file

@ -13,6 +13,12 @@ Changes affecting backwards compatibility
- De-deprecated ``re.nim`` because we have too much code using it - De-deprecated ``re.nim`` because we have too much code using it
and it got the basic API right. and it got the basic API right.
- ``split`` with ``set[char]`` as a delimiter in ``strutils.nim``
no longer strips and splits characters out of the target string
by the entire set of characters. Instead, it now behaves in a
similar fashion to ``split`` with ``string`` and ``char``
delimiters.
Library Additions Library Additions
----------------- -----------------
@ -23,8 +29,10 @@ Library Additions
- Added ``center`` and ``rsplit`` to ``strutils.nim`` to - Added ``center`` and ``rsplit`` to ``strutils.nim`` to
provide similar Python functionality for Nim's strings. provide similar Python functionality for Nim's strings.
- Added ``isTitle``, ``title``, and ``swapCase`` to ``unicode.nim`` to - Added ``isTitle``, ``title``, ``swapCase``, ``isUpper``, ``toUpper``,
provide unicode aware string case manipulation. ``isLower``, ``toLower``, ``isAlpha``, ``isSpace``, and ``capitalize``
to ``unicode.nim`` to provide unicode aware case manipulation and case
testing.
- Added a new module ``lib/pure/strmisc.nim`` to hold uncommon string - Added a new module ``lib/pure/strmisc.nim`` to hold uncommon string
operations. Currently contains ``partition``, ``rpartition`` operations. Currently contains ``partition``, ``rpartition``
@ -38,8 +46,17 @@ Library Additions
Compiler Additions Compiler Additions
------------------ ------------------
- The ``-d/--define`` flag can now optionally take a value to be used
by code at compile time.
Language Additions Language Additions
------------------ ------------------
- Added ``{.intdefine.}`` and ``{.strdefine.}`` macros to make use of
(optional) compile time defines.
- If the first statement is an ``import system`` statement then ``system``
is not imported implicitly anymore. This allows for code like
``import system except echo`` or ``from system import nil``.
Bugfixes Bugfixes
-------- --------