merged
This commit is contained in:
commit
c48102254a
72 changed files with 4785 additions and 1791 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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++
|
||||||
|
|
|
||||||
|
|
@ -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)",
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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}:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
10
doc/nimc.rst
10
doc/nimc.rst
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.} =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
461
lib/pure/ioselects/ioselectors_epoll.nim
Normal file
461
lib/pure/ioselects/ioselectors_epoll.nim
Normal 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
|
||||||
439
lib/pure/ioselects/ioselectors_kqueue.nim
Normal file
439
lib/pure/ioselects/ioselectors_kqueue.nim
Normal 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
|
||||||
295
lib/pure/ioselects/ioselectors_poll.nim
Normal file
295
lib/pure/ioselects/ioselectors_poll.nim
Normal 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
|
||||||
416
lib/pure/ioselects/ioselectors_select.nim
Normal file
416
lib/pure/ioselects/ioselectors_select.nim
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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".
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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("先秦兩漢") == "漢兩秦先"
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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".
|
||||||
|
|
|
||||||
|
|
@ -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.} =
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
2154
lib/upcoming/asyncdispatch.nim
Normal file
2154
lib/upcoming/asyncdispatch.nim
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1,4 @@
|
||||||
|
discard """
|
||||||
|
output: ""
|
||||||
|
"""
|
||||||
import "../template/utemplates", "../closure/uclosures"
|
import "../template/utemplates", "../closure/uclosures"
|
||||||
|
|
|
||||||
9
tests/async/tgeneric_async.nim
Normal file
9
tests/async/tgeneric_async.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
import asyncdispatch
|
||||||
|
|
||||||
|
when true:
|
||||||
|
# bug #2377
|
||||||
|
proc test[T](v: T) {.async.} =
|
||||||
|
echo $v
|
||||||
|
|
||||||
|
asyncCheck test[int](1)
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
23
tests/async/treturn_await.nim
Normal file
23
tests/async/treturn_await.nim
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
28
tests/generics/tforward_generic.nim
Normal file
28
tests/generics/tforward_generic.nim
Normal 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
|
||||||
42
tests/macros/tcomplexecho.nim
Normal file
42
tests/macros/tcomplexecho.nim
Normal 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()
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
output: "empty"
|
output: "void"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #898
|
# bug #898
|
||||||
|
|
|
||||||
21
tests/metatype/tvoid_must_not_match.nim
Normal file
21
tests/metatype/tvoid_must_not_match.nim
Normal 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"
|
||||||
9
tests/modules/texplicit_system_import.nim
Normal file
9
tests/modules/texplicit_system_import.nim
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
16
tests/vm/tvmmisc.nim
Normal 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))
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
--------
|
--------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue