Merge branch 'devel' into async-improvements
This commit is contained in:
commit
5e43e0d789
95 changed files with 9598 additions and 948 deletions
29
changelog.md
29
changelog.md
|
|
@ -108,3 +108,32 @@ This now needs to be written as:
|
||||||
- [``poly``](https://github.com/lcrees/polynumeric)
|
- [``poly``](https://github.com/lcrees/polynumeric)
|
||||||
- [``pdcurses``](https://github.com/lcrees/pdcurses)
|
- [``pdcurses``](https://github.com/lcrees/pdcurses)
|
||||||
- [``romans``](https://github.com/lcrees/romans)
|
- [``romans``](https://github.com/lcrees/romans)
|
||||||
|
|
||||||
|
- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
|
||||||
|
to write and test. The examples are tested as the last step of
|
||||||
|
``nim doc``.
|
||||||
|
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
|
||||||
|
extension that we called ``:test:``::
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:test:
|
||||||
|
# shows how the 'if' statement works
|
||||||
|
if true: echo "yes"
|
||||||
|
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
|
||||||
|
the specified slice is out of bounds. See issue
|
||||||
|
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
|
||||||
|
- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
|
||||||
|
separator now returns that empty string.
|
||||||
|
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
|
||||||
|
- The experimental overloading of the dot ``.`` operators now take
|
||||||
|
an ``untyped``` parameter as the field name, it used to be
|
||||||
|
a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
|
||||||
|
your code work with both old and new Nim versions.
|
||||||
|
See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
|
||||||
|
for more information.
|
||||||
|
- Added ``macros.unpackVarargs``.
|
||||||
|
- The memory manager now uses a variant of the TLSF algorithm that has much
|
||||||
|
better memory fragmentation behaviour. According
|
||||||
|
to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
|
||||||
|
fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
|
||||||
|
``dealloc`` became O(1) operations.
|
||||||
|
|
|
||||||
|
|
@ -639,7 +639,7 @@ type
|
||||||
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
|
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
|
||||||
mNHint, mNWarning, mNError,
|
mNHint, mNWarning, mNError,
|
||||||
mInstantiationInfo, mGetTypeInfo, mNGenSym,
|
mInstantiationInfo, mGetTypeInfo, mNGenSym,
|
||||||
mNimvm, mIntDefine, mStrDefine
|
mNimvm, mIntDefine, mStrDefine, mRunnableExamples
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
||||||
|
|
@ -1860,7 +1860,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
initLocExpr(p, e.sons[2], b)
|
initLocExpr(p, e.sons[2], b)
|
||||||
genDeepCopy(p, a, b)
|
genDeepCopy(p, a, b)
|
||||||
of mDotDot, mEqCString: genCall(p, e, d)
|
of mDotDot, mEqCString: genCall(p, e, d)
|
||||||
else: internalError(e.info, "genMagicExpr: " & $op)
|
else:
|
||||||
|
when defined(debugMagics):
|
||||||
|
echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
|
||||||
|
internalError(e.info, "genMagicExpr: " & $op)
|
||||||
|
|
||||||
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
# example: { a..b, c, d, e, f..g }
|
# example: { a..b, c, d, e, f..g }
|
||||||
|
|
|
||||||
|
|
@ -968,8 +968,11 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||||
addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
|
addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
|
||||||
discard cgsym(m, "TNimType")
|
discard cgsym(m, "TNimType")
|
||||||
if isDefined("nimTypeNames"):
|
if isDefined("nimTypeNames"):
|
||||||
|
var typename = typeToString(origType, preferName)
|
||||||
|
if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
|
||||||
|
typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info
|
||||||
addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
|
addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
|
||||||
[name, makeCstring typeToString(origType, preferName)])
|
[name, makeCstring typename])
|
||||||
discard cgsym(m, "nimTypeRoot")
|
discard cgsym(m, "nimTypeRoot")
|
||||||
addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
||||||
[name])
|
[name])
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ type
|
||||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||||
BModule* = ref TCGen
|
BModule* = ref TCGen
|
||||||
BProc* = ref TCProc
|
BProc* = ref TCProc
|
||||||
TBlock*{.final.} = object
|
TBlock* = object
|
||||||
id*: int # the ID of the label; positive means that it
|
id*: int # the ID of the label; positive means that it
|
||||||
label*: Rope # generated text for the label
|
label*: Rope # generated text for the label
|
||||||
# nil if label is not used
|
# nil if label is not used
|
||||||
|
|
@ -64,7 +64,7 @@ type
|
||||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||||
frameLen*: int16
|
frameLen*: int16
|
||||||
|
|
||||||
TCProc{.final.} = object # represents C proc that is currently generated
|
TCProc = object # represents C proc that is currently generated
|
||||||
prc*: PSym # the Nim proc that this C proc belongs to
|
prc*: PSym # the Nim proc that this C proc belongs to
|
||||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
|
|
|
||||||
|
|
@ -654,6 +654,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
gListFullPaths = true
|
gListFullPaths = true
|
||||||
of "dynliboverride":
|
of "dynliboverride":
|
||||||
dynlibOverride(switch, arg, pass, info)
|
dynlibOverride(switch, arg, pass, info)
|
||||||
|
of "dynliboverrideall":
|
||||||
|
expectNoArg(switch, arg, pass, info)
|
||||||
|
gDynlibOverrideAll = true
|
||||||
of "cs":
|
of "cs":
|
||||||
# only supported for compatibility. Does nothing.
|
# only supported for compatibility. Does nothing.
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,5 @@ proc initDefines*() =
|
||||||
when false: defineSymbol("nimHasOpt")
|
when false: defineSymbol("nimHasOpt")
|
||||||
defineSymbol("nimNoArrayToCstringConversion")
|
defineSymbol("nimNoArrayToCstringConversion")
|
||||||
defineSymbol("nimNewRoof")
|
defineSymbol("nimNewRoof")
|
||||||
|
defineSymbol("nimHasRunnableExamples")
|
||||||
|
defineSymbol("nimNewDot")
|
||||||
|
|
|
||||||
|
|
@ -167,10 +167,13 @@ template interestingSym(s: PSym): bool =
|
||||||
proc patchHead(n: PNode) =
|
proc patchHead(n: PNode) =
|
||||||
if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
|
if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
|
||||||
let s = n[0].sym
|
let s = n[0].sym
|
||||||
if sfFromGeneric in s.flags and s.name.s[0] == '=' and
|
if s.name.s[0] == '=' and s.name.s in ["=sink", "=", "=destroy"]:
|
||||||
s.name.s in ["=sink", "=", "=destroy"]:
|
if sfFromGeneric in s.flags:
|
||||||
excl(s.flags, sfFromGeneric)
|
excl(s.flags, sfFromGeneric)
|
||||||
patchHead(s.getBody)
|
patchHead(s.getBody)
|
||||||
|
if n[1].typ.isNil:
|
||||||
|
# XXX toptree crashes without this workaround. Figure out why.
|
||||||
|
return
|
||||||
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
|
||||||
template patch(op, field) =
|
template patch(op, field) =
|
||||||
if s.name.s == op and field != nil and field != s:
|
if s.name.s == op and field != nil and field != s:
|
||||||
|
|
@ -181,24 +184,30 @@ proc patchHead(n: PNode) =
|
||||||
for x in n:
|
for x in n:
|
||||||
patchHead(x)
|
patchHead(x)
|
||||||
|
|
||||||
|
proc patchHead(s: PSym) =
|
||||||
|
if sfFromGeneric in s.flags:
|
||||||
|
patchHead(s.ast[bodyPos])
|
||||||
|
|
||||||
|
template genOp(opr, opname) =
|
||||||
|
let op = opr
|
||||||
|
if op == nil:
|
||||||
|
globalError(dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
|
||||||
|
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||||
|
globalError(dest.info, "internal error: '" & opname & "' operator is generic")
|
||||||
|
patchHead op
|
||||||
|
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
|
||||||
|
|
||||||
proc genSink(t: PType; dest: PNode): PNode =
|
proc genSink(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
let op = if t.sink != nil: t.sink else: t.assignment
|
genOp(if t.sink != nil: t.sink else: t.assignment, "=sink")
|
||||||
assert op != nil
|
|
||||||
patchHead op.ast[bodyPos]
|
|
||||||
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
|
|
||||||
|
|
||||||
proc genCopy(t: PType; dest: PNode): PNode =
|
proc genCopy(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
assert t.assignment != nil
|
genOp(t.assignment, "=")
|
||||||
patchHead t.assignment.ast[bodyPos]
|
|
||||||
result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
|
|
||||||
|
|
||||||
proc genDestroy(t: PType; dest: PNode): PNode =
|
proc genDestroy(t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias})
|
let t = t.skipTypes({tyGenericInst, tyAlias})
|
||||||
assert t.destructor != nil
|
genOp(t.destructor, "=destroy")
|
||||||
patchHead t.destructor.ast[bodyPos]
|
|
||||||
result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
|
|
||||||
|
|
||||||
proc addTopVar(c: var Con; v: PNode) =
|
proc addTopVar(c: var Con; v: PNode) =
|
||||||
c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
|
c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
|
||||||
|
|
@ -210,7 +219,7 @@ template recurse(n, dest) =
|
||||||
dest.add p(n[i], c)
|
dest.add p(n[i], c)
|
||||||
|
|
||||||
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||||
if ri.kind in nkCallKinds:
|
if ri.kind in nkCallKinds+{nkObjConstr}:
|
||||||
result = genSink(ri.typ, dest)
|
result = genSink(ri.typ, dest)
|
||||||
# watch out and no not transform 'ri' twice if it's a call:
|
# watch out and no not transform 'ri' twice if it's a call:
|
||||||
let ri2 = copyNode(ri)
|
let ri2 = copyNode(ri)
|
||||||
|
|
@ -287,6 +296,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
recurse(n, result)
|
recurse(n, result)
|
||||||
|
|
||||||
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
|
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
|
||||||
|
echo "injecting into ", n
|
||||||
var c: Con
|
var c: Con
|
||||||
c.owner = owner
|
c.owner = owner
|
||||||
c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
|
c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
|
||||||
|
|
@ -312,7 +322,7 @@ proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
|
||||||
result.add body
|
result.add body
|
||||||
|
|
||||||
when defined(nimDebugDestroys):
|
when defined(nimDebugDestroys):
|
||||||
if owner.name.s == "createSeq":
|
if owner.name.s == "main" or true:
|
||||||
echo "------------------------------------"
|
echo "------------------------------------"
|
||||||
echo owner.name.s, " transformed to: "
|
echo owner.name.s, " transformed to: "
|
||||||
echo result
|
echo result
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ type
|
||||||
TSections = array[TSymKind, Rope]
|
TSections = array[TSymKind, Rope]
|
||||||
TDocumentor = object of rstgen.RstGenerator
|
TDocumentor = object of rstgen.RstGenerator
|
||||||
modDesc: Rope # module description
|
modDesc: Rope # module description
|
||||||
id: int # for generating IDs
|
|
||||||
toc, section: TSections
|
toc, section: TSections
|
||||||
indexValFilename: string
|
indexValFilename: string
|
||||||
analytics: string # Google Analytics javascript, "" if doesn't exist
|
analytics: string # Google Analytics javascript, "" if doesn't exist
|
||||||
|
|
@ -109,6 +108,8 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
|
||||||
result.id = 100
|
result.id = 100
|
||||||
result.jArray = newJArray()
|
result.jArray = newJArray()
|
||||||
initStrTable result.types
|
initStrTable result.types
|
||||||
|
result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
|
||||||
|
localError(newLineInfo(d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
|
||||||
|
|
||||||
proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
|
proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
|
||||||
if gCmd != cmdRst2tex: addf(dest, xml, args)
|
if gCmd != cmdRst2tex: addf(dest, xml, args)
|
||||||
|
|
@ -204,10 +205,87 @@ proc getPlainDocstring(n: PNode): string =
|
||||||
if n.comment != nil and startsWith(n.comment, "##"):
|
if n.comment != nil and startsWith(n.comment, "##"):
|
||||||
result = n.comment
|
result = n.comment
|
||||||
if result.len < 1:
|
if result.len < 1:
|
||||||
if n.kind notin {nkEmpty..nkNilLit}:
|
for i in countup(0, safeLen(n)-1):
|
||||||
for i in countup(0, len(n)-1):
|
result = getPlainDocstring(n.sons[i])
|
||||||
result = getPlainDocstring(n.sons[i])
|
if result.len > 0: return
|
||||||
if result.len > 0: return
|
|
||||||
|
proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}) =
|
||||||
|
var r: TSrcGen
|
||||||
|
var literal = ""
|
||||||
|
initTokRender(r, n, renderFlags)
|
||||||
|
var kind = tkEof
|
||||||
|
while true:
|
||||||
|
getNextTok(r, kind, literal)
|
||||||
|
case kind
|
||||||
|
of tkEof:
|
||||||
|
break
|
||||||
|
of tkComment:
|
||||||
|
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
|
||||||
|
[rope(esc(d.target, literal))])
|
||||||
|
of tokKeywordLow..tokKeywordHigh:
|
||||||
|
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
|
||||||
|
[rope(literal)])
|
||||||
|
of tkOpr:
|
||||||
|
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
|
||||||
|
[rope(esc(d.target, literal))])
|
||||||
|
of tkStrLit..tkTripleStrLit:
|
||||||
|
dispA(result, "<span class=\"StringLit\">$1</span>",
|
||||||
|
"\\spanStringLit{$1}", [rope(esc(d.target, literal))])
|
||||||
|
of tkCharLit:
|
||||||
|
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
|
||||||
|
[rope(esc(d.target, literal))])
|
||||||
|
of tkIntLit..tkUInt64Lit:
|
||||||
|
dispA(result, "<span class=\"DecNumber\">$1</span>",
|
||||||
|
"\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
|
||||||
|
of tkFloatLit..tkFloat128Lit:
|
||||||
|
dispA(result, "<span class=\"FloatNumber\">$1</span>",
|
||||||
|
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
|
||||||
|
of tkSymbol:
|
||||||
|
dispA(result, "<span class=\"Identifier\">$1</span>",
|
||||||
|
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
|
||||||
|
of tkSpaces, tkInvalid:
|
||||||
|
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,
|
||||||
|
tkBracketDotLe, tkBracketDotRi, tkParDotLe,
|
||||||
|
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
|
||||||
|
tkAccent, tkColonColon,
|
||||||
|
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
|
||||||
|
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
|
||||||
|
[rope(esc(d.target, literal))])
|
||||||
|
|
||||||
|
proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
|
||||||
|
case n.kind
|
||||||
|
of nkCallKinds:
|
||||||
|
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
|
||||||
|
n.len >= 2 and n.lastSon.kind == nkStmtList:
|
||||||
|
dispA(dest, "\n<strong class=\"examples_text\">$1</strong>\n",
|
||||||
|
"\n\\textbf{$1}\n", [rope"Examples:"])
|
||||||
|
inc d.listingCounter
|
||||||
|
let id = $d.listingCounter
|
||||||
|
dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"])
|
||||||
|
# this is a rather hacky way to get rid of the initial indentation
|
||||||
|
# that the renderer currently produces:
|
||||||
|
var i = 0
|
||||||
|
var body = n.lastSon
|
||||||
|
if body.len == 1 and body.kind == nkStmtList and
|
||||||
|
body.lastSon.kind == nkStmtList:
|
||||||
|
body = body.lastSon
|
||||||
|
for b in body:
|
||||||
|
if i > 0: dest.add "\n"
|
||||||
|
inc i
|
||||||
|
nodeToHighlightedHtml(d, b, dest, {})
|
||||||
|
dest.add(d.config.getOrDefault"doc.listing_end" % id)
|
||||||
|
else: discard
|
||||||
|
for i in 0 ..< n.safeLen:
|
||||||
|
getAllRunnableExamples(d, n[i], dest)
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc findDocComment(n: PNode): PNode =
|
proc findDocComment(n: PNode): PNode =
|
||||||
|
|
@ -379,11 +457,12 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
||||||
let
|
let
|
||||||
name = getName(d, nameNode)
|
name = getName(d, nameNode)
|
||||||
nameRope = name.rope
|
nameRope = name.rope
|
||||||
plainDocstring = getPlainDocstring(n) # call here before genRecComment!
|
var plainDocstring = getPlainDocstring(n) # call here before genRecComment!
|
||||||
var result: Rope = nil
|
var result: Rope = nil
|
||||||
var literal, plainName = ""
|
var literal, plainName = ""
|
||||||
var kind = tkEof
|
var kind = tkEof
|
||||||
var comm = genRecComment(d, n) # call this here for the side-effect!
|
var comm = genRecComment(d, n) # call this here for the side-effect!
|
||||||
|
getAllRunnableExamples(d, n, comm)
|
||||||
var r: TSrcGen
|
var r: TSrcGen
|
||||||
# Obtain the plain rendered string for hyperlink titles.
|
# Obtain the plain rendered string for hyperlink titles.
|
||||||
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
|
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
|
||||||
|
|
@ -395,53 +474,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
||||||
plainName.add(literal)
|
plainName.add(literal)
|
||||||
|
|
||||||
# Render the HTML hyperlink.
|
# Render the HTML hyperlink.
|
||||||
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
|
nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments})
|
||||||
while true:
|
|
||||||
getNextTok(r, kind, literal)
|
|
||||||
case kind
|
|
||||||
of tkEof:
|
|
||||||
break
|
|
||||||
of tkComment:
|
|
||||||
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
|
|
||||||
[rope(esc(d.target, literal))])
|
|
||||||
of tokKeywordLow..tokKeywordHigh:
|
|
||||||
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
|
|
||||||
[rope(literal)])
|
|
||||||
of tkOpr:
|
|
||||||
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
|
|
||||||
[rope(esc(d.target, literal))])
|
|
||||||
of tkStrLit..tkTripleStrLit:
|
|
||||||
dispA(result, "<span class=\"StringLit\">$1</span>",
|
|
||||||
"\\spanStringLit{$1}", [rope(esc(d.target, literal))])
|
|
||||||
of tkCharLit:
|
|
||||||
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
|
|
||||||
[rope(esc(d.target, literal))])
|
|
||||||
of tkIntLit..tkUInt64Lit:
|
|
||||||
dispA(result, "<span class=\"DecNumber\">$1</span>",
|
|
||||||
"\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
|
|
||||||
of tkFloatLit..tkFloat128Lit:
|
|
||||||
dispA(result, "<span class=\"FloatNumber\">$1</span>",
|
|
||||||
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
|
|
||||||
of tkSymbol:
|
|
||||||
dispA(result, "<span class=\"Identifier\">$1</span>",
|
|
||||||
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
|
|
||||||
of tkSpaces, tkInvalid:
|
|
||||||
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,
|
|
||||||
tkBracketDotLe, tkBracketDotRi, tkParDotLe,
|
|
||||||
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
|
|
||||||
tkAccent, tkColonColon,
|
|
||||||
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
|
|
||||||
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
|
|
||||||
[rope(esc(d.target, literal))])
|
|
||||||
|
|
||||||
inc(d.id)
|
inc(d.id)
|
||||||
let
|
let
|
||||||
|
|
@ -520,12 +553,24 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
|
||||||
proc checkForFalse(n: PNode): bool =
|
proc checkForFalse(n: PNode): bool =
|
||||||
result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0
|
result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0
|
||||||
|
|
||||||
proc traceDeps(d: PDoc, n: PNode) =
|
proc traceDeps(d: PDoc, it: PNode) =
|
||||||
const k = skModule
|
const k = skModule
|
||||||
if d.section[k] != nil: add(d.section[k], ", ")
|
|
||||||
dispA(d.section[k],
|
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
||||||
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
|
let sep = it[0]
|
||||||
"$1", [rope(getModuleName(n))])
|
let dir = it[1]
|
||||||
|
let a = newNodeI(nkInfix, it.info)
|
||||||
|
a.add sep
|
||||||
|
a.add dir
|
||||||
|
a.add sep # dummy entry, replaced in the loop
|
||||||
|
for x in it[2]:
|
||||||
|
a.sons[2] = x
|
||||||
|
traceDeps(d, a)
|
||||||
|
else:
|
||||||
|
if d.section[k] != nil: add(d.section[k], ", ")
|
||||||
|
dispA(d.section[k],
|
||||||
|
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
|
||||||
|
"$1", [rope(getModuleName(it))])
|
||||||
|
|
||||||
proc generateDoc*(d: PDoc, n: PNode) =
|
proc generateDoc*(d: PDoc, n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -609,10 +654,7 @@ proc generateJson*(d: PDoc, n: PNode) =
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
|
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
|
||||||
var
|
result = getName(d, nameNode) & "\n"
|
||||||
name = getName(d, nameNode)
|
|
||||||
|
|
||||||
result = name & "\n"
|
|
||||||
|
|
||||||
proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
|
proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -758,6 +800,26 @@ proc commandDoc*() =
|
||||||
proc commandRstAux(filename, outExt: string) =
|
proc commandRstAux(filename, outExt: string) =
|
||||||
var filen = addFileExt(filename, "txt")
|
var filen = addFileExt(filename, "txt")
|
||||||
var d = newDocumentor(filen, options.gConfigVars)
|
var d = newDocumentor(filen, options.gConfigVars)
|
||||||
|
d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
|
||||||
|
status: int; content: string) =
|
||||||
|
var outp: string
|
||||||
|
if filename.len == 0:
|
||||||
|
inc(d.id)
|
||||||
|
let nameOnly = splitFile(d.filename).name
|
||||||
|
let subdir = getNimcacheDir() / nameOnly
|
||||||
|
createDir(subdir)
|
||||||
|
outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim")
|
||||||
|
elif isAbsolute(filename):
|
||||||
|
outp = filename
|
||||||
|
else:
|
||||||
|
# Nim's convention: every path is relative to the file it was written in:
|
||||||
|
outp = splitFile(d.filename).dir / filename
|
||||||
|
writeFile(outp, content)
|
||||||
|
let cmd = unescape(cmd) % quoteShell(outp)
|
||||||
|
rawMessage(hintExecuting, cmd)
|
||||||
|
if execShellCmd(cmd) != status:
|
||||||
|
rawMessage(errExecutionOfProgramFailed, cmd)
|
||||||
|
|
||||||
d.isPureRst = true
|
d.isPureRst = true
|
||||||
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
|
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
|
||||||
{roSupportRawDirective})
|
{roSupportRawDirective})
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
|
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
|
||||||
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
||||||
else:
|
else:
|
||||||
internalAssert sfGenSym in s.flags
|
internalAssert sfGenSym in s.flags or s.kind == skType
|
||||||
var x = PSym(idTableGet(c.mapping, s))
|
var x = PSym(idTableGet(c.mapping, s))
|
||||||
if x == nil:
|
if x == nil:
|
||||||
x = copySym(s, false)
|
x = copySym(s, false)
|
||||||
|
|
|
||||||
|
|
@ -728,13 +728,13 @@ proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
|
||||||
else:
|
else:
|
||||||
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
||||||
if optListCmd in gGlobalOptions or gVerbosity > 1:
|
if optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||||
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
|
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
|
||||||
gNumberOfProcessors, afterRunEvent=runCb)
|
gNumberOfProcessors, afterRunEvent=runCb)
|
||||||
elif gVerbosity == 1:
|
elif gVerbosity == 1:
|
||||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
|
||||||
gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
|
gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
|
||||||
else:
|
else:
|
||||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
|
||||||
gNumberOfProcessors, afterRunEvent=runCb)
|
gNumberOfProcessors, afterRunEvent=runCb)
|
||||||
if res != 0:
|
if res != 0:
|
||||||
if gNumberOfProcessors <= 1:
|
if gNumberOfProcessors <= 1:
|
||||||
|
|
@ -764,8 +764,9 @@ proc callCCompiler*(projectfile: string) =
|
||||||
add(objfiles, quoteShell(
|
add(objfiles, quoteShell(
|
||||||
addFileExt(objFile, CC[cCompiler].objExt)))
|
addFileExt(objFile, CC[cCompiler].objExt)))
|
||||||
for x in toCompile:
|
for x in toCompile:
|
||||||
|
let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj
|
||||||
add(objfiles, ' ')
|
add(objfiles, ' ')
|
||||||
add(objfiles, quoteShell(x.obj))
|
add(objfiles, quoteShell(objFile))
|
||||||
|
|
||||||
linkCmd = getLinkCmd(projectfile, objfiles)
|
linkCmd = getLinkCmd(projectfile, objfiles)
|
||||||
if optCompileOnly notin gGlobalOptions:
|
if optCompileOnly notin gGlobalOptions:
|
||||||
|
|
|
||||||
|
|
@ -1563,14 +1563,22 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
internalError("createVar: " & $t.kind)
|
internalError("createVar: " & $t.kind)
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
|
template returnType: untyped =
|
||||||
|
~""
|
||||||
|
|
||||||
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
var
|
var
|
||||||
a: TCompRes
|
a: TCompRes
|
||||||
s: Rope
|
s: Rope
|
||||||
|
varCode: string
|
||||||
|
if v.constraint.isNil:
|
||||||
|
varCode = "var $2"
|
||||||
|
else:
|
||||||
|
varCode = v.constraint.strVal
|
||||||
if n.kind == nkEmpty:
|
if n.kind == nkEmpty:
|
||||||
let mname = mangleName(v, p.target)
|
let mname = mangleName(v, p.target)
|
||||||
lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n",
|
lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n",
|
||||||
[mname, createVar(p, v.typ, isIndirect(v))])
|
[returnType, mname, createVar(p, v.typ, isIndirect(v))])
|
||||||
if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex:
|
if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex:
|
||||||
lineF(p, "var $1_Idx = 0;$n", [ mname ])
|
lineF(p, "var $1_Idx = 0;$n", [ mname ])
|
||||||
else:
|
else:
|
||||||
|
|
@ -1587,25 +1595,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
|
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
|
||||||
if a.typ == etyBaseIndex:
|
if a.typ == etyBaseIndex:
|
||||||
if targetBaseIndex:
|
if targetBaseIndex:
|
||||||
lineF(p, "var $1 = $2, $1_Idx = $3;$n",
|
lineF(p, varCode & " = $3, $2_Idx = $4;$n",
|
||||||
[v.loc.r, a.address, a.res])
|
[returnType, v.loc.r, a.address, a.res])
|
||||||
else:
|
else:
|
||||||
lineF(p, "var $1 = [$2, $3];$n",
|
lineF(p, varCode & " = [$3, $4];$n",
|
||||||
[v.loc.r, a.address, a.res])
|
[returnType, v.loc.r, a.address, a.res])
|
||||||
else:
|
else:
|
||||||
if targetBaseIndex:
|
if targetBaseIndex:
|
||||||
let tmp = p.getTemp
|
let tmp = p.getTemp
|
||||||
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
||||||
[tmp, a.res, v.loc.r])
|
[tmp, a.res, v.loc.r])
|
||||||
else:
|
else:
|
||||||
lineF(p, "var $1 = $2;$n", [v.loc.r, a.res])
|
lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res])
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
s = a.res
|
s = a.res
|
||||||
if isIndirect(v):
|
if isIndirect(v):
|
||||||
lineF(p, "var $1 = [$2];$n", [v.loc.r, s])
|
lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
|
||||||
else:
|
else:
|
||||||
lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])
|
lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s])
|
||||||
|
|
||||||
proc genVarStmt(p: PProc, n: PNode) =
|
proc genVarStmt(p: PProc, n: PNode) =
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
|
@ -2162,8 +2170,22 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||||
returnStmt = "return $#;$n" % [a.res]
|
returnStmt = "return $#;$n" % [a.res]
|
||||||
|
|
||||||
p.nested: genStmt(p, prc.getBody)
|
p.nested: genStmt(p, prc.getBody)
|
||||||
let def = "function $#($#) {$n$#$#$#$#$#" %
|
|
||||||
[name, header,
|
var def: Rope
|
||||||
|
if not prc.constraint.isNil:
|
||||||
|
def = (prc.constraint.strVal & " {$n$#$#$#$#$#") %
|
||||||
|
[ returnType,
|
||||||
|
name,
|
||||||
|
header,
|
||||||
|
optionaLine(p.globals),
|
||||||
|
optionaLine(p.locals),
|
||||||
|
optionaLine(resultAsgn),
|
||||||
|
optionaLine(genProcBody(p, prc)),
|
||||||
|
optionaLine(p.indentLine(returnStmt))]
|
||||||
|
else:
|
||||||
|
def = "function $#($#) {$n$#$#$#$#$#" %
|
||||||
|
[ name,
|
||||||
|
header,
|
||||||
optionaLine(p.globals),
|
optionaLine(p.globals),
|
||||||
optionaLine(p.locals),
|
optionaLine(p.locals),
|
||||||
optionaLine(resultAsgn),
|
optionaLine(resultAsgn),
|
||||||
|
|
|
||||||
|
|
@ -860,6 +860,23 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
||||||
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||||
tok.strongSpaceB = -1
|
tok.strongSpaceB = -1
|
||||||
|
|
||||||
|
proc newlineFollows*(L: var TLexer): bool =
|
||||||
|
var pos = L.bufpos
|
||||||
|
var buf = L.buf
|
||||||
|
while true:
|
||||||
|
case buf[pos]
|
||||||
|
of ' ', '\t':
|
||||||
|
inc(pos)
|
||||||
|
of CR, LF:
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
of '#':
|
||||||
|
inc(pos)
|
||||||
|
if buf[pos] == '#': inc(pos)
|
||||||
|
if buf[pos] != '[': return true
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||||
isDoc: bool) =
|
isDoc: bool) =
|
||||||
var pos = start
|
var pos = start
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ var
|
||||||
gNoNimblePath* = false
|
gNoNimblePath* = false
|
||||||
gExperimentalMode*: bool
|
gExperimentalMode*: bool
|
||||||
newDestructors*: bool
|
newDestructors*: bool
|
||||||
|
gDynlibOverrideAll*: bool
|
||||||
|
|
||||||
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
|
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
|
||||||
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
|
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
|
||||||
|
|
@ -427,7 +428,7 @@ proc inclDynlibOverride*(lib: string) =
|
||||||
gDllOverrides[lib.canonDynlibName] = "true"
|
gDllOverrides[lib.canonDynlibName] = "true"
|
||||||
|
|
||||||
proc isDynlibOverride*(lib: string): bool =
|
proc isDynlibOverride*(lib: string): bool =
|
||||||
result = gDllOverrides.hasKey(lib.canonDynlibName)
|
result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName)
|
||||||
|
|
||||||
proc binaryStrSearch*(x: openArray[string], y: string): int =
|
proc binaryStrSearch*(x: openArray[string], y: string): int =
|
||||||
var a = 0
|
var a = 0
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const
|
||||||
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||||
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||||
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
|
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
|
||||||
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
||||||
wOverride, wConstructor, wExportNims, wUsed, wLiftLocals}
|
wOverride, wConstructor, wExportNims, wUsed, wLiftLocals}
|
||||||
converterPragmas* = procPragmas
|
converterPragmas* = procPragmas
|
||||||
|
|
@ -759,10 +759,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
incl(sym.loc.flags, lfNoDecl)
|
incl(sym.loc.flags, lfNoDecl)
|
||||||
# implies nodecl, because otherwise header would not make sense
|
# implies nodecl, because otherwise header would not make sense
|
||||||
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
|
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
|
||||||
of wDestructor:
|
|
||||||
sym.flags.incl sfOverriden
|
|
||||||
if sym.name.s.normalize != "destroy":
|
|
||||||
localError(n.info, errGenerated, "destructor has to be named 'destroy'")
|
|
||||||
of wOverride:
|
of wOverride:
|
||||||
sym.flags.incl sfOverriden
|
sym.flags.incl sfOverriden
|
||||||
of wNosideeffect:
|
of wNosideeffect:
|
||||||
|
|
|
||||||
|
|
@ -826,7 +826,10 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||||
t = tkOpr
|
t = tkOpr
|
||||||
put(g, t, s)
|
put(g, t, s)
|
||||||
if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
|
if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
|
||||||
put(g, tkIntLit, $n.sym.id)
|
when defined(debugMagics):
|
||||||
|
put(g, tkIntLit, $n.sym.id & $n.sym.magic)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||||
|
|
||||||
proc runNimScript*(cache: IdentCache; scriptName: string;
|
proc runNimScript*(cache: IdentCache; scriptName: string;
|
||||||
freshDefines=true; config: ConfigRef=nil) =
|
freshDefines=true; config: ConfigRef=nil) =
|
||||||
|
rawMessage(hintConf, scriptName)
|
||||||
passes.gIncludeFile = includeModule
|
passes.gIncludeFile = includeModule
|
||||||
passes.gImportModule = importModule
|
passes.gImportModule = importModule
|
||||||
let graph = newModuleGraph(config)
|
let graph = newModuleGraph(config)
|
||||||
|
|
|
||||||
|
|
@ -570,6 +570,18 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
|
||||||
result = ast.emptyNode
|
result = ast.emptyNode
|
||||||
#if gCmd == cmdIdeTools: findSuggest(c, n)
|
#if gCmd == cmdIdeTools: findSuggest(c, n)
|
||||||
|
|
||||||
|
proc testExamples(c: PContext) =
|
||||||
|
let inp = toFullPath(c.module.info)
|
||||||
|
let outp = inp.changeFileExt"" & "_examples.nim"
|
||||||
|
renderModule(c.runnableExamples, inp, outp)
|
||||||
|
let backend = if isDefined("js"): "js"
|
||||||
|
elif isDefined("cpp"): "cpp"
|
||||||
|
elif isDefined("objc"): "objc"
|
||||||
|
else: "c"
|
||||||
|
if os.execShellCmd("nim " & backend & " -r " & outp) != 0:
|
||||||
|
quit "[Examples] failed"
|
||||||
|
removeFile(outp)
|
||||||
|
|
||||||
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||||
var c = PContext(context)
|
var c = PContext(context)
|
||||||
if gCmd == cmdIdeTools and not c.suggestionsMade:
|
if gCmd == cmdIdeTools and not c.suggestionsMade:
|
||||||
|
|
@ -584,5 +596,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||||
result.add(c.module.ast)
|
result.add(c.module.ast)
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
|
if c.runnableExamples != nil: testExamples(c)
|
||||||
|
|
||||||
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
|
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements lifting for assignments. Later versions of this code
|
## This module implements lifting for type-bound operations
|
||||||
## will be able to also lift ``=deepCopy`` and ``=destroy``.
|
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
||||||
|
|
||||||
# included from sem.nim
|
# included from sem.nim
|
||||||
|
|
||||||
|
|
@ -302,6 +302,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
n.sons[paramsPos] = result.typ.n
|
n.sons[paramsPos] = result.typ.n
|
||||||
n.sons[bodyPos] = body
|
n.sons[bodyPos] = body
|
||||||
result.ast = n
|
result.ast = n
|
||||||
|
incl result.flags, sfFromGeneric
|
||||||
|
|
||||||
|
|
||||||
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
||||||
|
|
@ -319,8 +320,10 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||||
## to ensure we lift assignment, destructors and moves properly.
|
## to ensure we lift assignment, destructors and moves properly.
|
||||||
## The later 'destroyer' pass depends on it.
|
## The later 'destroyer' pass depends on it.
|
||||||
if not newDestructors or not hasDestructor(typ): return
|
if not newDestructors or not hasDestructor(typ): return
|
||||||
# do not produce wrong liftings while we're still instantiating generics:
|
when false:
|
||||||
if c.typesWithOps.len > 0: return
|
# do not produce wrong liftings while we're still instantiating generics:
|
||||||
|
# now disabled; breaks topttree.nim!
|
||||||
|
if c.typesWithOps.len > 0: return
|
||||||
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
if typ.destructor == nil:
|
if typ.destructor == nil:
|
||||||
|
|
@ -329,3 +332,6 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||||
liftBody(c, typ, attachedAsgn, info)
|
liftBody(c, typ, attachedAsgn, info)
|
||||||
if typ.sink == nil:
|
if typ.sink == nil:
|
||||||
liftBody(c, typ, attachedSink, info)
|
liftBody(c, typ, attachedSink, info)
|
||||||
|
|
||||||
|
#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode =
|
||||||
|
# if n.kind == nkCall and
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
if candidates != "":
|
if candidates != "":
|
||||||
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
||||||
localError(n.info, errGenerated, result)
|
localError(n.info, errGenerated, result & "\nexpression: " & $n)
|
||||||
|
|
||||||
proc bracketNotFoundError(c: PContext; n: PNode) =
|
proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||||
var errors: CandidateErrors = @[]
|
var errors: CandidateErrors = @[]
|
||||||
|
|
@ -235,12 +235,11 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
|
|
||||||
if nfDotField in n.flags:
|
if nfDotField in n.flags:
|
||||||
internalAssert f.kind == nkIdent and n.sonsLen >= 2
|
internalAssert f.kind == nkIdent and n.sonsLen >= 2
|
||||||
let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
|
|
||||||
|
|
||||||
# leave the op head symbol empty,
|
# leave the op head symbol empty,
|
||||||
# we are going to try multiple variants
|
# we are going to try multiple variants
|
||||||
n.sons[0..1] = [nil, n[1], calleeName]
|
n.sons[0..1] = [nil, n[1], f]
|
||||||
orig.sons[0..1] = [nil, orig[1], calleeName]
|
orig.sons[0..1] = [nil, orig[1], f]
|
||||||
|
|
||||||
template tryOp(x) =
|
template tryOp(x) =
|
||||||
let op = newIdentNode(getIdent(x), n.info)
|
let op = newIdentNode(getIdent(x), n.info)
|
||||||
|
|
@ -255,8 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
tryOp "."
|
tryOp "."
|
||||||
|
|
||||||
elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
|
elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
|
||||||
let calleeName = newStrNode(nkStrLit,
|
# we need to strip away the trailing '=' here:
|
||||||
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
|
let calleeName = newIdentNode(getIdent(f.ident.s[0..f.ident.s.len-2]), n.info)
|
||||||
let callOp = newIdentNode(getIdent".=", n.info)
|
let callOp = newIdentNode(getIdent".=", n.info)
|
||||||
n.sons[0..1] = [callOp, n[1], calleeName]
|
n.sons[0..1] = [callOp, n[1], calleeName]
|
||||||
orig.sons[0..1] = [callOp, orig[1], calleeName]
|
orig.sons[0..1] = [callOp, orig[1], calleeName]
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ type
|
||||||
# the generic type has been constructed completely. See
|
# the generic type has been constructed completely. See
|
||||||
# tests/destructor/topttree.nim for an example that
|
# tests/destructor/topttree.nim for an example that
|
||||||
# would otherwise fail.
|
# would otherwise fail.
|
||||||
|
runnableExamples*: PNode
|
||||||
|
|
||||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||||
result.genericSym = s
|
result.genericSym = s
|
||||||
|
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements destructors.
|
|
||||||
|
|
||||||
# included from sem.nim
|
|
||||||
|
|
||||||
# special marker values that indicates that we are
|
|
||||||
# 1) AnalyzingDestructor: currently analyzing the type for destructor
|
|
||||||
# generation (needed for recursive types)
|
|
||||||
# 2) DestructorIsTrivial: completed the analysis before and determined
|
|
||||||
# that the type has a trivial destructor
|
|
||||||
var analyzingDestructor, destructorIsTrivial: PSym
|
|
||||||
new(analyzingDestructor)
|
|
||||||
new(destructorIsTrivial)
|
|
||||||
|
|
||||||
var
|
|
||||||
destructorName = getIdent"destroy_"
|
|
||||||
destructorParam = getIdent"this_"
|
|
||||||
destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
|
|
||||||
|
|
||||||
proc instantiateDestructor(c: PContext, typ: PType): PType
|
|
||||||
|
|
||||||
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
|
|
||||||
var t = s.typ.sons[1].skipTypes({tyVar})
|
|
||||||
if t.kind == tyGenericInvocation:
|
|
||||||
for i in 1 ..< t.sonsLen:
|
|
||||||
if t.sons[i].kind != tyGenericParam:
|
|
||||||
localError(n.info, errDestructorNotGenericEnough)
|
|
||||||
return
|
|
||||||
t = t.base
|
|
||||||
elif t.kind == tyCompositeTypeClass:
|
|
||||||
t = t.base
|
|
||||||
if t.kind != tyGenericBody:
|
|
||||||
localError(n.info, errDestructorNotGenericEnough)
|
|
||||||
return
|
|
||||||
|
|
||||||
t.destructor = s
|
|
||||||
# automatically insert calls to base classes' destructors
|
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
|
||||||
for i in countup(0, t.sonsLen - 1):
|
|
||||||
# when inheriting directly from object
|
|
||||||
# there will be a single nil son
|
|
||||||
if t.sons[i] == nil: continue
|
|
||||||
let destructableT = instantiateDestructor(c, t.sons[i])
|
|
||||||
if destructableT != nil:
|
|
||||||
n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
|
|
||||||
useSym(destructableT.destructor, c.graph.usageSym),
|
|
||||||
n.sons[paramsPos][1][0]]))
|
|
||||||
|
|
||||||
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
|
|
||||||
|
|
||||||
proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
|
|
||||||
let destructableT = instantiateDestructor(c, field.typ)
|
|
||||||
if destructableT != nil:
|
|
||||||
result = newNode(nkCall, field.info, @[
|
|
||||||
useSym(destructableT.destructor, c.graph.usageSym),
|
|
||||||
newNode(nkDotExpr, field.info, @[holder, useSym(field, c.graph.usageSym)])])
|
|
||||||
|
|
||||||
proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
|
|
||||||
var nonTrivialFields = 0
|
|
||||||
result = newNode(nkCaseStmt, n.info, @[])
|
|
||||||
# case x.kind
|
|
||||||
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
|
|
||||||
for i in countup(1, n.len - 1):
|
|
||||||
# of A, B:
|
|
||||||
let ni = n[i]
|
|
||||||
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
|
|
||||||
|
|
||||||
let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
|
|
||||||
if stmt == nil:
|
|
||||||
caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
|
|
||||||
else:
|
|
||||||
caseBranch.addSon(stmt)
|
|
||||||
nonTrivialFields += stmt.len
|
|
||||||
|
|
||||||
result.addSon(caseBranch)
|
|
||||||
|
|
||||||
# maybe no fields were destroyed?
|
|
||||||
if nonTrivialFields == 0:
|
|
||||||
result = nil
|
|
||||||
|
|
||||||
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
|
|
||||||
template maybeAddLine(e) =
|
|
||||||
let stmt = e
|
|
||||||
if stmt != nil:
|
|
||||||
if result == nil: result = newNode(nkStmtList)
|
|
||||||
result.addSon(stmt)
|
|
||||||
|
|
||||||
case field.kind
|
|
||||||
of nkRecCase:
|
|
||||||
maybeAddLine destroyCase(c, field, holder)
|
|
||||||
of nkSym:
|
|
||||||
maybeAddLine destroySym(c, field.sym, holder)
|
|
||||||
of nkRecList:
|
|
||||||
for son in field:
|
|
||||||
maybeAddLine destroyFieldOrFields(c, son, holder)
|
|
||||||
else:
|
|
||||||
internalAssert false
|
|
||||||
|
|
||||||
proc generateDestructor(c: PContext, t: PType): PNode =
|
|
||||||
## generate a destructor for a user-defined object or tuple type
|
|
||||||
## returns nil if the destructor turns out to be trivial
|
|
||||||
|
|
||||||
# XXX: This may be true for some C-imported types such as
|
|
||||||
# Tposix_spawnattr
|
|
||||||
if t.n == nil or t.n.sons == nil: return
|
|
||||||
internalAssert t.n.kind == nkRecList
|
|
||||||
let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
|
|
||||||
# call the destructods of all fields
|
|
||||||
result = destroyFieldOrFields(c, t.n, destructedObj)
|
|
||||||
# base classes' destructors will be automatically called by
|
|
||||||
# semProcAux for both auto-generated and user-defined destructors
|
|
||||||
|
|
||||||
proc instantiateDestructor(c: PContext, typ: PType): PType =
|
|
||||||
# returns nil if a variable of type `typ` doesn't require a
|
|
||||||
# destructor. Otherwise, returns the type, which holds the
|
|
||||||
# destructor that must be used for the varialbe.
|
|
||||||
# The destructor is either user-defined or automatically
|
|
||||||
# generated by the compiler in a member-wise fashion.
|
|
||||||
var t = typ.skipGenericAlias
|
|
||||||
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t
|
|
||||||
|
|
||||||
if typeHoldingUserDefinition.destructor != nil:
|
|
||||||
# XXX: This is not entirely correct for recursive types, but we need
|
|
||||||
# it temporarily to hide the "destroy is already defined" problem
|
|
||||||
if typeHoldingUserDefinition.destructor notin
|
|
||||||
[analyzingDestructor, destructorIsTrivial]:
|
|
||||||
return typeHoldingUserDefinition
|
|
||||||
else:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
t = t.skipTypes({tyGenericInst, tyAlias})
|
|
||||||
case t.kind
|
|
||||||
of tySequence, tyArray, tyOpenArray, tyVarargs:
|
|
||||||
t.destructor = analyzingDestructor
|
|
||||||
if instantiateDestructor(c, t.sons[0]) != nil:
|
|
||||||
t.destructor = getCompilerProc"nimDestroyRange"
|
|
||||||
return t
|
|
||||||
else:
|
|
||||||
return nil
|
|
||||||
of tyTuple, tyObject:
|
|
||||||
t.destructor = analyzingDestructor
|
|
||||||
let generated = generateDestructor(c, t)
|
|
||||||
if generated != nil:
|
|
||||||
internalAssert t.sym != nil
|
|
||||||
var i = t.sym.info
|
|
||||||
let fullDef = newNode(nkProcDef, i, @[
|
|
||||||
newIdentNode(destructorName, i),
|
|
||||||
emptyNode,
|
|
||||||
emptyNode,
|
|
||||||
newNode(nkFormalParams, i, @[
|
|
||||||
emptyNode,
|
|
||||||
newNode(nkIdentDefs, i, @[
|
|
||||||
newIdentNode(destructorParam, i),
|
|
||||||
symNodeFromType(c, makeVarType(c, t), t.sym.info),
|
|
||||||
emptyNode]),
|
|
||||||
]),
|
|
||||||
newNode(nkPragma, i, @[destructorPragma]),
|
|
||||||
emptyNode,
|
|
||||||
generated
|
|
||||||
])
|
|
||||||
let semantizedDef = semProc(c, fullDef)
|
|
||||||
t.destructor = semantizedDef[namePos].sym
|
|
||||||
return t
|
|
||||||
else:
|
|
||||||
t.destructor = destructorIsTrivial
|
|
||||||
return nil
|
|
||||||
else:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
proc createDestructorCall(c: PContext, s: PSym): PNode =
|
|
||||||
let varTyp = s.typ
|
|
||||||
if varTyp == nil or sfGlobal in s.flags: return
|
|
||||||
let destructableT = instantiateDestructor(c, varTyp)
|
|
||||||
if destructableT != nil:
|
|
||||||
let call = semStmt(c, newNode(nkCall, s.info, @[
|
|
||||||
useSym(destructableT.destructor, c.graph.usageSym),
|
|
||||||
useSym(s, c.graph.usageSym)]))
|
|
||||||
result = newNode(nkDefer, s.info, @[call])
|
|
||||||
|
|
@ -53,7 +53,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
else:
|
else:
|
||||||
if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
|
if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
|
||||||
if result.typ.kind == tyVar: result = newDeref(result)
|
if result.typ.kind == tyVar: result = newDeref(result)
|
||||||
semDestructorCheck(c, result, flags)
|
|
||||||
|
|
||||||
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
result = semExpr(c, n, flags)
|
result = semExpr(c, n, flags)
|
||||||
|
|
@ -66,7 +65,6 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
result.typ = errorType(c)
|
result.typ = errorType(c)
|
||||||
else:
|
else:
|
||||||
semProcvarCheck(c, result)
|
semProcvarCheck(c, result)
|
||||||
semDestructorCheck(c, result, flags)
|
|
||||||
|
|
||||||
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
|
|
@ -671,6 +669,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||||
if callee.magic != mNone:
|
if callee.magic != mNone:
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info)
|
if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info)
|
||||||
|
#result = patchResolvedTypeBoundOp(c, result)
|
||||||
if c.matchedConcept == nil:
|
if c.matchedConcept == nil:
|
||||||
result = evalAtCompileTime(c, result)
|
result = evalAtCompileTime(c, result)
|
||||||
|
|
||||||
|
|
@ -1847,6 +1846,18 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
analyseIfAddressTakenInCall(c, result)
|
analyseIfAddressTakenInCall(c, result)
|
||||||
if callee.magic != mNone:
|
if callee.magic != mNone:
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
|
of mRunnableExamples:
|
||||||
|
if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
|
||||||
|
if n.sons[0].kind == nkIdent:
|
||||||
|
if sfMainModule in c.module.flags:
|
||||||
|
let inp = toFullPath(c.module.info)
|
||||||
|
if c.runnableExamples == nil:
|
||||||
|
c.runnableExamples = newTree(nkStmtList,
|
||||||
|
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
|
||||||
|
c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon)
|
||||||
|
result = setMs(n, s)
|
||||||
|
else:
|
||||||
|
result = emptyNode
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,27 +97,12 @@ template semProcvarCheck(c: PContext, n: PNode) =
|
||||||
|
|
||||||
proc semProc(c: PContext, n: PNode): PNode
|
proc semProc(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
include semdestruct
|
|
||||||
|
|
||||||
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
|
|
||||||
if not newDestructors:
|
|
||||||
if efAllowDestructor notin flags and
|
|
||||||
n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
|
|
||||||
if instantiateDestructor(c, n.typ) != nil:
|
|
||||||
localError(n.info, warnDestructor)
|
|
||||||
# This still breaks too many things:
|
|
||||||
when false:
|
|
||||||
if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
|
|
||||||
c.p.owner.kind notin {skTemplate, skMacro}:
|
|
||||||
localError(n.info, errGenerated, "value expected, but got a type")
|
|
||||||
|
|
||||||
proc semExprBranch(c: PContext, n: PNode): PNode =
|
proc semExprBranch(c: PContext, n: PNode): PNode =
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if result.typ != nil:
|
if result.typ != nil:
|
||||||
# XXX tyGenericInst here?
|
# XXX tyGenericInst here?
|
||||||
semProcvarCheck(c, result)
|
semProcvarCheck(c, result)
|
||||||
if result.typ.kind == tyVar: result = newDeref(result)
|
if result.typ.kind == tyVar: result = newDeref(result)
|
||||||
semDestructorCheck(c, result, {})
|
|
||||||
|
|
||||||
proc semExprBranchScope(c: PContext, n: PNode): PNode =
|
proc semExprBranchScope(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
|
@ -421,15 +406,6 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||||
else:
|
else:
|
||||||
result.add identDefs
|
result.add identDefs
|
||||||
|
|
||||||
proc addDefer(c: PContext; result: var PNode; s: PSym) =
|
|
||||||
let deferDestructorCall = createDestructorCall(c, s)
|
|
||||||
if deferDestructorCall != nil:
|
|
||||||
if result.kind != nkStmtList:
|
|
||||||
let oldResult = result
|
|
||||||
result = newNodeI(nkStmtList, result.info)
|
|
||||||
result.add oldResult
|
|
||||||
result.add deferDestructorCall
|
|
||||||
|
|
||||||
proc isDiscardUnderscore(v: PSym): bool =
|
proc isDiscardUnderscore(v: PSym): bool =
|
||||||
if v.name.s == "_":
|
if v.name.s == "_":
|
||||||
v.flags.incl(sfGenSym)
|
v.flags.incl(sfGenSym)
|
||||||
|
|
@ -609,7 +585,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if def.kind == nkPar: v.ast = def[j]
|
if def.kind == nkPar: v.ast = def[j]
|
||||||
setVarType(v, tup.sons[j])
|
setVarType(v, tup.sons[j])
|
||||||
b.sons[j] = newSymNode(v)
|
b.sons[j] = newSymNode(v)
|
||||||
if not newDestructors: addDefer(c, result, v)
|
|
||||||
checkNilable(v)
|
checkNilable(v)
|
||||||
if sfCompileTime in v.flags: hasCompileTime = true
|
if sfCompileTime in v.flags: hasCompileTime = true
|
||||||
if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result)
|
if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result)
|
||||||
|
|
@ -1041,6 +1016,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
checkConstructedType(s.info, s.typ)
|
checkConstructedType(s.info, s.typ)
|
||||||
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
||||||
checkForMetaFields(s.typ.n)
|
checkForMetaFields(s.typ.n)
|
||||||
|
instAllTypeBoundOp(c, n.info)
|
||||||
|
|
||||||
|
|
||||||
proc semAllTypeSections(c: PContext; n: PNode): PNode =
|
proc semAllTypeSections(c: PContext; n: PNode): PNode =
|
||||||
proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} =
|
proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} =
|
||||||
|
|
@ -1095,9 +1072,11 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
|
||||||
## to allow the type definitions in the section to reference each other
|
## to allow the type definitions in the section to reference each other
|
||||||
## without regard for the order of their definitions.
|
## without regard for the order of their definitions.
|
||||||
if sfNoForward notin c.module.flags or nfSem notin n.flags:
|
if sfNoForward notin c.module.flags or nfSem notin n.flags:
|
||||||
|
inc c.inTypeContext
|
||||||
typeSectionLeftSidePass(c, n)
|
typeSectionLeftSidePass(c, n)
|
||||||
typeSectionRightSidePass(c, n)
|
typeSectionRightSidePass(c, n)
|
||||||
typeSectionFinalPass(c, n)
|
typeSectionFinalPass(c, n)
|
||||||
|
dec c.inTypeContext
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
|
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
|
||||||
|
|
@ -1318,7 +1297,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
var obj = t.sons[1].sons[0]
|
var obj = t.sons[1].sons[0]
|
||||||
while true:
|
while true:
|
||||||
incl(obj.flags, tfHasAsgn)
|
incl(obj.flags, tfHasAsgn)
|
||||||
if obj.kind == tyGenericBody: obj = obj.lastSon
|
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
|
||||||
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
||||||
else: break
|
else: break
|
||||||
if obj.kind in {tyObject, tyDistinct}:
|
if obj.kind in {tyObject, tyDistinct}:
|
||||||
|
|
@ -1331,10 +1310,6 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
if not noError and sfSystemModule notin s.owner.flags:
|
if not noError and sfSystemModule notin s.owner.flags:
|
||||||
localError(n.info, errGenerated,
|
localError(n.info, errGenerated,
|
||||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||||
else:
|
|
||||||
doDestructorStuff(c, s, n)
|
|
||||||
if not experimentalMode(c):
|
|
||||||
localError n.info, "use the {.experimental.} pragma to enable destructors"
|
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
of "deepcopy", "=deepcopy":
|
of "deepcopy", "=deepcopy":
|
||||||
if s.typ.len == 2 and
|
if s.typ.len == 2 and
|
||||||
|
|
@ -1561,8 +1536,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
s.options = gOptions
|
s.options = gOptions
|
||||||
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
||||||
if s.name.s[0] in {'.', '('}:
|
if s.name.s[0] in {'.', '('}:
|
||||||
if s.name.s in [".", ".()", ".=", "()"] and not experimentalMode(c):
|
if s.name.s in [".", ".()", ".="] and not experimentalMode(c) and not newDestructors:
|
||||||
message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
|
message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
|
||||||
|
elif s.name.s == "()" and not experimentalMode(c):
|
||||||
|
message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
|
||||||
|
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
# for DLL generation it is annoying to check for sfImportc!
|
# for DLL generation it is annoying to check for sfImportc!
|
||||||
if sfBorrow in s.flags:
|
if sfBorrow in s.flags:
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ type
|
||||||
CoProc
|
CoProc
|
||||||
CoType
|
CoType
|
||||||
CoOwnerSig
|
CoOwnerSig
|
||||||
|
CoIgnoreRange
|
||||||
|
|
||||||
proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
|
proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
|
||||||
|
|
||||||
|
|
@ -159,14 +160,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
c &= char(t.kind)
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyBool, tyChar, tyInt..tyUInt64:
|
of tyBool, tyChar, tyInt..tyUInt64:
|
||||||
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
||||||
# produced instead of ``NI``:
|
# produced instead of ``NI``:
|
||||||
|
c &= char(t.kind)
|
||||||
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
|
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
|
||||||
c.hashSym(t.sym)
|
c.hashSym(t.sym)
|
||||||
of tyObject, tyEnum:
|
of tyObject, tyEnum:
|
||||||
|
c &= char(t.kind)
|
||||||
if t.typeInst != nil:
|
if t.typeInst != nil:
|
||||||
assert t.typeInst.kind == tyGenericInst
|
assert t.typeInst.kind == tyGenericInst
|
||||||
for i in countup(1, sonsLen(t.typeInst) - 2):
|
for i in countup(1, sonsLen(t.typeInst) - 2):
|
||||||
|
|
@ -199,26 +201,35 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
|
||||||
if t.len > 0 and t.sons[0] != nil:
|
if t.len > 0 and t.sons[0] != nil:
|
||||||
hashType c, t.sons[0], flags
|
hashType c, t.sons[0], flags
|
||||||
of tyRef, tyPtr, tyGenericBody, tyVar:
|
of tyRef, tyPtr, tyGenericBody, tyVar:
|
||||||
|
c &= char(t.kind)
|
||||||
c.hashType t.lastSon, flags
|
c.hashType t.lastSon, flags
|
||||||
if tfVarIsPtr in t.flags: c &= ".varisptr"
|
if tfVarIsPtr in t.flags: c &= ".varisptr"
|
||||||
of tyFromExpr:
|
of tyFromExpr:
|
||||||
|
c &= char(t.kind)
|
||||||
c.hashTree(t.n)
|
c.hashTree(t.n)
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
|
c &= char(t.kind)
|
||||||
if t.n != nil and CoType notin flags:
|
if t.n != nil and CoType notin flags:
|
||||||
assert(sonsLen(t.n) == sonsLen(t))
|
assert(sonsLen(t.n) == sonsLen(t))
|
||||||
for i in countup(0, sonsLen(t.n) - 1):
|
for i in countup(0, sonsLen(t.n) - 1):
|
||||||
assert(t.n.sons[i].kind == nkSym)
|
assert(t.n.sons[i].kind == nkSym)
|
||||||
c &= t.n.sons[i].sym.name.s
|
c &= t.n.sons[i].sym.name.s
|
||||||
c &= ':'
|
c &= ':'
|
||||||
c.hashType(t.sons[i], flags)
|
c.hashType(t.sons[i], flags+{CoIgnoreRange})
|
||||||
c &= ','
|
c &= ','
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags
|
for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags+{CoIgnoreRange}
|
||||||
of tyRange, tyStatic:
|
of tyRange:
|
||||||
#if CoType notin flags:
|
if CoIgnoreRange notin flags:
|
||||||
|
c &= char(t.kind)
|
||||||
|
c.hashTree(t.n)
|
||||||
|
c.hashType(t.sons[0], flags)
|
||||||
|
of tyStatic:
|
||||||
|
c &= char(t.kind)
|
||||||
c.hashTree(t.n)
|
c.hashTree(t.n)
|
||||||
c.hashType(t.sons[0], flags)
|
c.hashType(t.sons[0], flags)
|
||||||
of tyProc:
|
of tyProc:
|
||||||
|
c &= char(t.kind)
|
||||||
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
||||||
if CoProc in flags and t.n != nil:
|
if CoProc in flags and t.n != nil:
|
||||||
let params = t.n
|
let params = t.n
|
||||||
|
|
@ -236,7 +247,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
|
||||||
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
||||||
if tfThread in t.flags: c &= ".thread"
|
if tfThread in t.flags: c &= ".thread"
|
||||||
if tfVarargs in t.flags: c &= ".varargs"
|
if tfVarargs in t.flags: c &= ".varargs"
|
||||||
|
of tyArray:
|
||||||
|
c &= char(t.kind)
|
||||||
|
for i in 0..<t.len: c.hashType(t.sons[i], flags-{CoIgnoreRange})
|
||||||
else:
|
else:
|
||||||
|
c &= char(t.kind)
|
||||||
for i in 0..<t.len: c.hashType(t.sons[i], flags)
|
for i in 0..<t.len: c.hashType(t.sons[i], flags)
|
||||||
if tfNotNil in t.flags and CoType notin flags: c &= "not nil"
|
if tfNotNil in t.flags and CoType notin flags: c &= "not nil"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -693,7 +693,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
|
||||||
inc(j)
|
inc(j)
|
||||||
add(result, a.PTransNode)
|
add(result, a.PTransNode)
|
||||||
if len(result) == 2: result = result[1]
|
if len(result) == 2: result = result[1]
|
||||||
elif magic in {mNBindSym, mTypeOf}:
|
elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
|
||||||
# for bindSym(myconst) we MUST NOT perform constant folding:
|
# for bindSym(myconst) we MUST NOT perform constant folding:
|
||||||
result = n.PTransNode
|
result = n.PTransNode
|
||||||
elif magic == mProcCall:
|
elif magic == mProcCall:
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,10 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
|
|
||||||
if inst:
|
if inst:
|
||||||
if t.sym != nil: # if this node has a symbol
|
if t.sym != nil: # if this node has a symbol
|
||||||
if allowRecursion: # getTypeImpl behavior: turn off recursion
|
if not allowRecursion: # getTypeInst behavior: return symbol
|
||||||
allowRecursion = false
|
|
||||||
else: # getTypeInst behavior: return symbol
|
|
||||||
return atomicType(t.sym)
|
return atomicType(t.sym)
|
||||||
|
#else: # getTypeImpl behavior: turn off recursion
|
||||||
|
# allowRecursion = false
|
||||||
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyNone: result = atomicType("none", mNone)
|
of tyNone: result = atomicType("none", mNone)
|
||||||
|
|
@ -121,22 +121,25 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
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)
|
||||||
for i in 0 ..< t.len:
|
for i in 0 ..< t.len:
|
||||||
result.add mapTypeToAst(t.sons[i], info)
|
result.add mapTypeToAst(t.sons[i], info)
|
||||||
of tyGenericInst, tyAlias:
|
of tyGenericInst:
|
||||||
if inst:
|
if inst:
|
||||||
if allowRecursion:
|
if allowRecursion:
|
||||||
result = mapTypeToAstR(t.lastSon, info)
|
result = mapTypeToAstR(t.lastSon, info)
|
||||||
else:
|
else:
|
||||||
result = newNodeX(nkBracketExpr)
|
result = newNodeX(nkBracketExpr)
|
||||||
result.add mapTypeToAst(t.lastSon, info)
|
#result.add mapTypeToAst(t.lastSon, info)
|
||||||
|
result.add mapTypeToAst(t[0], info)
|
||||||
for i in 1 ..< t.len-1:
|
for i in 1 ..< t.len-1:
|
||||||
result.add mapTypeToAst(t.sons[i], info)
|
result.add mapTypeToAst(t.sons[i], info)
|
||||||
else:
|
else:
|
||||||
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
|
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
|
||||||
of tyGenericBody:
|
of tyGenericBody:
|
||||||
if inst:
|
if inst:
|
||||||
result = mapTypeToAstX(t.lastSon, info, inst, true)
|
result = mapTypeToAstR(t.lastSon, info)
|
||||||
else:
|
else:
|
||||||
result = mapTypeToAst(t.lastSon, info)
|
result = mapTypeToAst(t.lastSon, info)
|
||||||
|
of tyAlias:
|
||||||
|
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
|
||||||
of tyOrdinal:
|
of tyOrdinal:
|
||||||
result = mapTypeToAst(t.lastSon, info)
|
result = mapTypeToAst(t.lastSon, info)
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
|
|
|
||||||
|
|
@ -1130,6 +1130,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
# produces a value
|
# produces a value
|
||||||
else:
|
else:
|
||||||
globalError(n.info, "expandToAst requires a call expression")
|
globalError(n.info, "expandToAst requires a call expression")
|
||||||
|
of mRunnableExamples:
|
||||||
|
discard "just ignore any call to runnableExamples"
|
||||||
else:
|
else:
|
||||||
# mGCref, mGCunref,
|
# mGCref, mGCunref,
|
||||||
globalError(n.info, "cannot generate code for: " & $m)
|
globalError(n.info, "cannot generate code for: " & $m)
|
||||||
|
|
|
||||||
|
|
@ -88,10 +88,58 @@ doc.body_toc = """
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@if boot:
|
||||||
|
# This is enabled with the "boot" directive to generate
|
||||||
|
# the compiler documentation.
|
||||||
|
# As a user, tweak the block below instead.
|
||||||
|
# You can add your own global-links entries
|
||||||
doc.body_toc_group = """
|
doc.body_toc_group = """
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="three columns">
|
<div class="three columns">
|
||||||
<div>
|
<div id="global-links">
|
||||||
|
<ul class="simple">
|
||||||
|
<li>
|
||||||
|
<a href="manual.html">Manual</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="lib.html">Standard library</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="theindex.html">Index</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="searchInput">
|
||||||
|
Search: <input type="text" id="searchInput"
|
||||||
|
onkeyup="search()" />
|
||||||
|
</div>
|
||||||
|
<div class="search-groupby">
|
||||||
|
Group by:
|
||||||
|
<select onchange="groupBy(this.value)">
|
||||||
|
<option value="section">Section</option>
|
||||||
|
<option value="type">Type</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
$tableofcontents
|
||||||
|
</div>
|
||||||
|
<div class="nine columns" id="content">
|
||||||
|
<div id="tocRoot"></div>
|
||||||
|
<p class="module-desc">$moduledesc</p>
|
||||||
|
$content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
doc.body_toc_group = """
|
||||||
|
<div class="row">
|
||||||
|
<div class="three columns">
|
||||||
|
<div id="global-links">
|
||||||
|
<ul class="simple">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="searchInput">
|
||||||
Search: <input type="text" id="searchInput"
|
Search: <input type="text" id="searchInput"
|
||||||
onkeyup="search()" />
|
onkeyup="search()" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,6 +159,7 @@ doc.body_toc_group = """
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
@end
|
||||||
|
|
||||||
doc.body_no_toc = """
|
doc.body_no_toc = """
|
||||||
$moduledesc
|
$moduledesc
|
||||||
|
|
@ -135,7 +184,7 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<link rel="shortcut icon" href="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=="/>
|
<link rel="shortcut icon" href="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=="/>
|
||||||
|
|
||||||
<!-- Google fonts -->
|
<!-- Google fonts -->
|
||||||
<link href='https://fonts.googleapis.com/css?family=Raleway:400,600,900' rel='stylesheet' type='text/css'/>
|
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
|
||||||
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
|
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
|
||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
|
|
@ -168,18 +217,19 @@ html {
|
||||||
|
|
||||||
/* Where we want fancier font if available */
|
/* Where we want fancier font if available */
|
||||||
h1, h2, h3, h4, h5, h6, p.module-desc, table.docinfo + blockquote p, table.docinfo blockquote p, h1 + blockquote p {
|
h1, h2, h3, h4, h5, h6, p.module-desc, table.docinfo + blockquote p, table.docinfo blockquote p, h1 + blockquote p {
|
||||||
font-family: "Raleway", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important; }
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important; }
|
||||||
|
|
||||||
h1.title {
|
h1.title {
|
||||||
font-weight: 900; }
|
font-weight: 900; }
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: #666;
|
color: #444;
|
||||||
background-color: rgba(252, 248, 244, 0.75); }
|
letter-spacing: 0.15px;
|
||||||
|
background-color: rgba(252, 248, 244, 0.45); }
|
||||||
|
|
||||||
/* Skeleton grid */
|
/* Skeleton grid */
|
||||||
.container {
|
.container {
|
||||||
|
|
@ -295,8 +345,8 @@ cite {
|
||||||
font-style: italic !important; }
|
font-style: italic !important; }
|
||||||
|
|
||||||
dt > pre {
|
dt > pre {
|
||||||
border-color: rgba(0, 0, 0, 0.15);
|
border-color: rgba(0, 0, 0, 0.1);
|
||||||
background-color: transparent;
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
margin: 15px 0px 5px; }
|
margin: 15px 0px 5px; }
|
||||||
|
|
||||||
dd > pre {
|
dd > pre {
|
||||||
|
|
@ -313,6 +363,17 @@ dd > pre {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
table-layout: fixed; }
|
table-layout: fixed; }
|
||||||
|
|
||||||
|
/* Nim search input */
|
||||||
|
div#searchInput {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
div#searchInput input#searchInput {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
div.search-groupby {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
table.line-nums-table {
|
table.line-nums-table {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
|
|
@ -456,7 +517,7 @@ img {
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 8px; }
|
||||||
|
|
||||||
small {
|
small {
|
||||||
font-size: 85%; }
|
font-size: 85%; }
|
||||||
|
|
@ -476,7 +537,7 @@ h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
@ -484,6 +545,7 @@ h6 {
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
|
font-weight: 400;
|
||||||
padding-bottom: .15em;
|
padding-bottom: .15em;
|
||||||
border-bottom: 1px solid #aaaaaa;
|
border-bottom: 1px solid #aaaaaa;
|
||||||
margin-top: 1.0em;
|
margin-top: 1.0em;
|
||||||
|
|
@ -614,13 +676,13 @@ pre {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: calc(100% - 19.5px);
|
min-width: calc(100% - 19.5px);
|
||||||
padding: 9.5px;
|
padding: 9.5px;
|
||||||
margin: 0.25em 10px 0.25em 10px;
|
margin: 0.25em 10px 10px 10px;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
white-space: pre !important;
|
white-space: pre !important;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
background-color: whitesmoke;
|
background-color: rgba(0, 0, 0, 0.01);
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
-webkit-border-radius: 4px;
|
-webkit-border-radius: 4px;
|
||||||
-moz-border-radius: 4px;
|
-moz-border-radius: 4px;
|
||||||
|
|
@ -899,14 +961,14 @@ div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||||
div.important p.admonition-title, div.note p.admonition-title,
|
div.important p.admonition-title, div.note p.admonition-title,
|
||||||
div.tip p.admonition-title {
|
div.tip p.admonition-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif; }
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
|
||||||
|
|
||||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||||
div.danger p.admonition-title, div.error p.admonition-title,
|
div.danger p.admonition-title, div.error p.admonition-title,
|
||||||
div.warning p.admonition-title, .code .error {
|
div.warning p.admonition-title, .code .error {
|
||||||
color: #b30000;
|
color: #b30000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif; }
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||||
compound paragraphs.
|
compound paragraphs.
|
||||||
|
|
@ -953,7 +1015,7 @@ div.sidebar {
|
||||||
clear: right; }
|
clear: right; }
|
||||||
|
|
||||||
div.sidebar p.rubric {
|
div.sidebar p.rubric {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-size: medium; }
|
font-size: medium; }
|
||||||
|
|
||||||
div.system-messages {
|
div.system-messages {
|
||||||
|
|
@ -1060,12 +1122,12 @@ p.rubric {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
|
|
||||||
p.sidebar-title {
|
p.sidebar-title {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: larger; }
|
font-size: larger; }
|
||||||
|
|
||||||
p.sidebar-subtitle {
|
p.sidebar-subtitle {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-weight: bold; }
|
font-weight: bold; }
|
||||||
|
|
||||||
p.topic-title {
|
p.topic-title {
|
||||||
|
|
@ -1107,15 +1169,15 @@ pre.code .inserted, code .inserted {
|
||||||
background-color: #A3D289; }
|
background-color: #A3D289; }
|
||||||
|
|
||||||
span.classifier {
|
span.classifier {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-style: oblique; }
|
font-style: oblique; }
|
||||||
|
|
||||||
span.classifier-delimiter {
|
span.classifier-delimiter {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||||
font-weight: bold; }
|
font-weight: bold; }
|
||||||
|
|
||||||
span.interpreted {
|
span.interpreted {
|
||||||
font-family: "Helvetica Neue", "HelveticaNeue", "Raleway", Helvetica, Arial, sans-serif; }
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
|
||||||
|
|
||||||
span.option {
|
span.option {
|
||||||
white-space: nowrap; }
|
white-space: nowrap; }
|
||||||
|
|
@ -1138,7 +1200,7 @@ table.docinfo {
|
||||||
margin: 0em;
|
margin: 0em;
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
font-family: "Raleway", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important;
|
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important;
|
||||||
color: #444444; }
|
color: #444444; }
|
||||||
|
|
||||||
table.docutils {
|
table.docutils {
|
||||||
|
|
@ -1268,15 +1330,15 @@ 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 {
|
div.pragma {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
span.pragmabegin {
|
span.pragmabegin {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
span.pragmaend {
|
span.pragmaend {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.search_results {
|
div.search_results {
|
||||||
background-color: antiquewhite;
|
background-color: antiquewhite;
|
||||||
|
|
@ -1284,6 +1346,11 @@ div.search_results {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border: 1px solid #4d4d4d;
|
border: 1px solid #4d4d4d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div#global-links ul {
|
||||||
|
margin-left: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script type="text/javascript" src="../dochack.js"></script>
|
<script type="text/javascript" src="../dochack.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ Advanced options:
|
||||||
symbol matching is fuzzy so
|
symbol matching is fuzzy so
|
||||||
that --dynlibOverride:lua matches
|
that --dynlibOverride:lua matches
|
||||||
dynlib: "liblua.so.3"
|
dynlib: "liblua.so.3"
|
||||||
|
--dynlibOverrideAll makes the dynlib pragma have no effect
|
||||||
--listCmd list the commands used to execute external programs
|
--listCmd list the commands used to execute external programs
|
||||||
--parallelBuild:0|1|... perform a parallel build
|
--parallelBuild:0|1|... perform a parallel build
|
||||||
value = number of processors (0 for auto-detect)
|
value = number of processors (0 for auto-detect)
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ or dynamic file formats such as JSON or XML.
|
||||||
When Nim encounters an expression that cannot be resolved by the
|
When Nim encounters an expression that cannot be resolved by the
|
||||||
standard overload resolution rules, the current scope will be searched
|
standard overload resolution rules, the current scope will be searched
|
||||||
for a dot operator that can be matched against a re-written form of
|
for a dot operator that can be matched against a re-written form of
|
||||||
the expression, where the unknown field or proc name is converted to
|
the expression, where the unknown field or proc name is passed to
|
||||||
an additional static string parameter:
|
an ``untyped`` parameter:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
a.b # becomes `.`(a, "b")
|
a.b # becomes `.`(a, "b")
|
||||||
|
|
@ -28,7 +28,7 @@ The matched dot operators can be symbols of any callable kind (procs,
|
||||||
templates and macros), depending on the desired effect:
|
templates and macros), depending on the desired effect:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc `.` (js: PJsonNode, field: string): JSON = js[field]
|
template `.` (js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
|
||||||
|
|
||||||
var js = parseJson("{ x: 1, y: 2}")
|
var js = parseJson("{ x: 1, y: 2}")
|
||||||
echo js.x # outputs 1
|
echo js.x # outputs 1
|
||||||
|
|
|
||||||
118
doc/tut1.rst
118
doc/tut1.rst
|
|
@ -30,6 +30,7 @@ The first program
|
||||||
We start the tour with a modified "hello world" program:
|
We start the tour with a modified "hello world" program:
|
||||||
|
|
||||||
.. code-block:: Nim
|
.. code-block:: Nim
|
||||||
|
:test: "nim c $1"
|
||||||
# This is a comment
|
# This is a comment
|
||||||
echo "What's your name? "
|
echo "What's your name? "
|
||||||
var name: string = readLine(stdin)
|
var name: string = readLine(stdin)
|
||||||
|
|
@ -72,6 +73,7 @@ you can leave out the type in the declaration (this is called `local type
|
||||||
inference`:idx:). So this will work too:
|
inference`:idx:). So this will work too:
|
||||||
|
|
||||||
.. code-block:: Nim
|
.. code-block:: Nim
|
||||||
|
:test: "nim c $1"
|
||||||
var name = readLine(stdin)
|
var name = readLine(stdin)
|
||||||
|
|
||||||
Note that this is basically the only form of type inference that exists in
|
Note that this is basically the only form of type inference that exists in
|
||||||
|
|
@ -116,6 +118,7 @@ Comments start anywhere outside a string or character literal with the
|
||||||
hash character ``#``. Documentation comments start with ``##``:
|
hash character ``#``. Documentation comments start with ``##``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
# A comment.
|
# A comment.
|
||||||
|
|
||||||
var myVariable: int ## a documentation comment
|
var myVariable: int ## a documentation comment
|
||||||
|
|
@ -129,6 +132,7 @@ Multiline comments are started with ``#[`` and terminated with ``]#``. Multilin
|
||||||
comments can also be nested.
|
comments can also be nested.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
#[
|
#[
|
||||||
You can have any Nim code text commented
|
You can have any Nim code text commented
|
||||||
out inside this with no indentation restrictions.
|
out inside this with no indentation restrictions.
|
||||||
|
|
@ -142,6 +146,7 @@ You can also use the `discard statement <#procedures-discard-statement>`_ togeth
|
||||||
literals* to create block comments:
|
literals* to create block comments:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
discard """ You can have any Nim code text commented
|
discard """ You can have any Nim code text commented
|
||||||
out inside this with no indentation restrictions.
|
out inside this with no indentation restrictions.
|
||||||
yes("May I ask a pointless question?") """
|
yes("May I ask a pointless question?") """
|
||||||
|
|
@ -169,6 +174,7 @@ Indentation can be used after the ``var`` keyword to list a whole section of
|
||||||
variables:
|
variables:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
x, y: int
|
x, y: int
|
||||||
# a comment can occur here too
|
# a comment can occur here too
|
||||||
|
|
@ -190,6 +196,7 @@ overloaded. You can declare multiple variables with a single assignment
|
||||||
statement and all the variables will have the same value:
|
statement and all the variables will have the same value:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
:test: "nim c $1"
|
||||||
var x, y = 3 # assigns 3 to the variables `x` and `y`
|
var x, y = 3 # assigns 3 to the variables `x` and `y`
|
||||||
echo "x ", x # outputs "x 3"
|
echo "x ", x # outputs "x 3"
|
||||||
echo "y ", y # outputs "y 3"
|
echo "y ", y # outputs "y 3"
|
||||||
|
|
@ -212,12 +219,14 @@ cannot change. The compiler must be able to evaluate the expression in a
|
||||||
constant declaration at compile time:
|
constant declaration at compile time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
const x = "abc" # the constant x contains the string "abc"
|
const x = "abc" # the constant x contains the string "abc"
|
||||||
|
|
||||||
Indentation can be used after the ``const`` keyword to list a whole section of
|
Indentation can be used after the ``const`` keyword to list a whole section of
|
||||||
constants:
|
constants:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
:test: "nim c $1"
|
||||||
const
|
const
|
||||||
x = 1
|
x = 1
|
||||||
# a comment can occur here too
|
# a comment can occur here too
|
||||||
|
|
@ -243,6 +252,7 @@ and put it into a data section":
|
||||||
const input = readLine(stdin) # Error: constant expression expected
|
const input = readLine(stdin) # Error: constant expression expected
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
:test: "nim c $1"
|
||||||
let input = readLine(stdin) # works
|
let input = readLine(stdin) # works
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -260,6 +270,7 @@ If statement
|
||||||
The if statement is one way to branch the control flow:
|
The if statement is one way to branch the control flow:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
let name = readLine(stdin)
|
let name = readLine(stdin)
|
||||||
if name == "":
|
if name == "":
|
||||||
echo "Poor soul, you lost your name?"
|
echo "Poor soul, you lost your name?"
|
||||||
|
|
@ -281,6 +292,7 @@ Another way to branch is provided by the case statement. A case statement is
|
||||||
a multi-branch:
|
a multi-branch:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
let name = readLine(stdin)
|
let name = readLine(stdin)
|
||||||
case name
|
case name
|
||||||
of "":
|
of "":
|
||||||
|
|
@ -338,6 +350,7 @@ While statement
|
||||||
The while statement is a simple looping construct:
|
The while statement is a simple looping construct:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
echo "What's your name? "
|
echo "What's your name? "
|
||||||
var name = readLine(stdin)
|
var name = readLine(stdin)
|
||||||
|
|
@ -358,6 +371,7 @@ provides. The example uses the built-in `countup <system.html#countup>`_
|
||||||
iterator:
|
iterator:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
echo "Counting to ten: "
|
echo "Counting to ten: "
|
||||||
for i in countup(1, 10):
|
for i in countup(1, 10):
|
||||||
echo i
|
echo i
|
||||||
|
|
@ -409,6 +423,7 @@ Other useful iterators for collections (like arrays and sequences) are
|
||||||
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
|
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
for index, item in ["a","b"].pairs:
|
for index, item in ["a","b"].pairs:
|
||||||
echo item, " at index ", index
|
echo item, " at index ", index
|
||||||
# => a at index 0
|
# => a at index 0
|
||||||
|
|
@ -421,6 +436,8 @@ new scope. This means that in the following example, ``x`` is not accessible
|
||||||
outside the loop:
|
outside the loop:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
while false:
|
while false:
|
||||||
var x = "hi"
|
var x = "hi"
|
||||||
echo x # does not work
|
echo x # does not work
|
||||||
|
|
@ -430,6 +447,8 @@ are only visible within the block they have been declared. The ``block``
|
||||||
statement can be used to open a new block explicitly:
|
statement can be used to open a new block explicitly:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
:status: 1
|
||||||
block myblock:
|
block myblock:
|
||||||
var x = "hi"
|
var x = "hi"
|
||||||
echo x # does not work either
|
echo x # does not work either
|
||||||
|
|
@ -444,6 +463,7 @@ can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
||||||
innermost construct, unless a label of a block is given:
|
innermost construct, unless a label of a block is given:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
block myblock:
|
block myblock:
|
||||||
echo "entering block"
|
echo "entering block"
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -465,6 +485,7 @@ Like in many other programming languages, a ``continue`` statement starts
|
||||||
the next iteration immediately:
|
the next iteration immediately:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
while true:
|
while true:
|
||||||
let x = readLine(stdin)
|
let x = readLine(stdin)
|
||||||
if x == "": continue
|
if x == "": continue
|
||||||
|
|
@ -477,6 +498,7 @@ When statement
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
when system.hostOS == "windows":
|
when system.hostOS == "windows":
|
||||||
echo "running on Windows!"
|
echo "running on Windows!"
|
||||||
|
|
@ -549,6 +571,7 @@ an expression is allowed:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# computes fac(4) at compile time:
|
# computes fac(4) at compile time:
|
||||||
|
:test: "nim c $1"
|
||||||
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -561,6 +584,7 @@ is needed. (Some languages call them *methods* or *functions*.) In Nim new
|
||||||
procedures are defined with the ``proc`` keyword:
|
procedures are defined with the ``proc`` keyword:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc yes(question: string): bool =
|
proc yes(question: string): bool =
|
||||||
echo question, " (y/n)"
|
echo question, " (y/n)"
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -597,6 +621,7 @@ automatically at the end of a procedure if there is no ``return`` statement at
|
||||||
the exit.
|
the exit.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc sumTillNegative(x: varargs[int]): int =
|
proc sumTillNegative(x: varargs[int]): int =
|
||||||
for i in x:
|
for i in x:
|
||||||
if i < 0:
|
if i < 0:
|
||||||
|
|
@ -624,6 +649,7 @@ to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
||||||
is possible, and actually an idiom:
|
is possible, and actually an idiom:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc printSeq(s: seq, nprinted: int = -1) =
|
proc printSeq(s: seq, nprinted: int = -1) =
|
||||||
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||||
for i in 0 .. <nprinted:
|
for i in 0 .. <nprinted:
|
||||||
|
|
@ -633,6 +659,7 @@ If the procedure needs to modify the argument for the
|
||||||
caller, a ``var`` parameter can be used:
|
caller, a ``var`` parameter can be used:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc divmod(a, b: int; res, remainder: var int) =
|
proc divmod(a, b: int; res, remainder: var int) =
|
||||||
res = a div b # integer division
|
res = a div b # integer division
|
||||||
remainder = a mod b # integer modulo operation
|
remainder = a mod b # integer modulo operation
|
||||||
|
|
@ -663,6 +690,7 @@ The return value can be ignored implicitly if the called proc/iterator has
|
||||||
been declared with the ``discardable`` pragma:
|
been declared with the ``discardable`` pragma:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc p(x, y: int): int {.discardable.} =
|
proc p(x, y: int): int {.discardable.} =
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
|
|
@ -772,6 +800,7 @@ The "``" notation can also be used to call an operator just like any other
|
||||||
procedure:
|
procedure:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
if `==`( `+`(3, 4), 7): echo "True"
|
if `==`( `+`(3, 4), 7): echo "True"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -819,6 +848,7 @@ Iterators
|
||||||
Let's return to the simple counting example:
|
Let's return to the simple counting example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
echo "Counting to ten: "
|
echo "Counting to ten: "
|
||||||
for i in countup(1, 10):
|
for i in countup(1, 10):
|
||||||
echo i
|
echo i
|
||||||
|
|
@ -840,6 +870,7 @@ the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
||||||
and here it is - our first iterator:
|
and here it is - our first iterator:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
iterator countup(a, b: int): int =
|
iterator countup(a, b: int): int =
|
||||||
var res = a
|
var res = a
|
||||||
while res <= b:
|
while res <= b:
|
||||||
|
|
@ -894,7 +925,8 @@ evaluation. For example:
|
||||||
Characters
|
Characters
|
||||||
----------
|
----------
|
||||||
The `character type` is called ``char``. Its size is always one byte, so
|
The `character type` is called ``char``. Its size is always one byte, so
|
||||||
it cannot represent most UTF-8 characters; but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character.
|
it cannot represent most UTF-8 characters; but it *can* represent one of the bytes
|
||||||
|
that makes up a multi-byte UTF-8 character.
|
||||||
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
||||||
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
||||||
designed for this.
|
designed for this.
|
||||||
|
|
@ -945,6 +977,7 @@ to specify a non-default integer type:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
let
|
let
|
||||||
x = 0 # x is of type ``int``
|
x = 0 # x is of type ``int``
|
||||||
y = 0'i8 # y is of type ``int8``
|
y = 0'i8 # y is of type ``int8``
|
||||||
|
|
@ -981,6 +1014,7 @@ Float literals can have a *type suffix* to specify a non-default float
|
||||||
type:
|
type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
x = 0.0 # x is of type ``float``
|
x = 0.0 # x is of type ``float``
|
||||||
y = 0.0'f32 # y is of type ``float32``
|
y = 0.0'f32 # y is of type ``float32``
|
||||||
|
|
@ -1002,6 +1036,7 @@ Conversion between basic types is performed by using the
|
||||||
type as a function:
|
type as a function:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
x: int32 = 1.int32 # same as calling int32(1)
|
x: int32 = 1.int32 # same as calling int32(1)
|
||||||
y: int8 = int8('a') # 'a' == 97'i8
|
y: int8 = int8('a') # 'a' == 97'i8
|
||||||
|
|
@ -1023,6 +1058,7 @@ graphs with cycles. The following example shows that even for basic types
|
||||||
there is a difference between the ``$`` and ``repr`` outputs:
|
there is a difference between the ``$`` and ``repr`` outputs:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
myBool = true
|
myBool = true
|
||||||
myCharacter = 'n'
|
myCharacter = 'n'
|
||||||
|
|
@ -1047,6 +1083,7 @@ Advanced types
|
||||||
In Nim new types can be defined within a ``type`` statement:
|
In Nim new types can be defined within a ``type`` statement:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
biggestInt = int64 # biggest integer type that is available
|
biggestInt = int64 # biggest integer type that is available
|
||||||
biggestFloat = float64 # biggest float type that is available
|
biggestFloat = float64 # biggest float type that is available
|
||||||
|
|
@ -1063,6 +1100,7 @@ to an integer value internally. The first symbol is represented
|
||||||
at runtime by 0, the second by 1 and so on. For example:
|
at runtime by 0, the second by 1 and so on. For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Direction = enum
|
Direction = enum
|
||||||
|
|
@ -1087,6 +1125,7 @@ explicitly given is assigned the value of the previous symbol + 1.
|
||||||
An explicit ordered enum can have *holes*:
|
An explicit ordered enum can have *holes*:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
MyEnum = enum
|
MyEnum = enum
|
||||||
a = 2, b = 4, c = 89
|
a = 2, b = 4, c = 89
|
||||||
|
|
@ -1125,6 +1164,7 @@ A subrange type is a range of values from an integer or enumeration type
|
||||||
(the base type). Example:
|
(the base type). Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
MySubrange = range[0..5]
|
MySubrange = range[0..5]
|
||||||
|
|
||||||
|
|
@ -1155,6 +1195,7 @@ an array has the same type. The array's index type can be any ordinal type.
|
||||||
Arrays can be constructed using ``[]``:
|
Arrays can be constructed using ``[]``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
IntArray = array[0..5, int] # an array that is indexed with 0..5
|
IntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||||
|
|
@ -1177,6 +1218,7 @@ length. `low(a) <system.html#low>`_ returns the lowest valid index for the
|
||||||
array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
Direction = enum
|
Direction = enum
|
||||||
north, east, south, west
|
north, east, south, west
|
||||||
|
|
@ -1228,6 +1270,7 @@ It is quite common to have arrays start at zero, so there's a shortcut syntax
|
||||||
to specify a range from zero to the specified index minus one:
|
to specify a range from zero to the specified index minus one:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
IntArray = array[0..5, int] # an array that is indexed with 0..5
|
IntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||||
QuickArray = array[6, int] # an array that is indexed with 0..5
|
QuickArray = array[6, int] # an array that is indexed with 0..5
|
||||||
|
|
@ -1260,6 +1303,7 @@ A sequence may be passed to an openarray parameter.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
var
|
var
|
||||||
x: seq[int] # a reference to a sequence of integers
|
x: seq[int] # a reference to a sequence of integers
|
||||||
|
|
@ -1282,6 +1326,7 @@ value. Here the ``for`` statement is looping over the results from the
|
||||||
<system.html>`_ module. Examples:
|
<system.html>`_ module. Examples:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
for value in @[3, 4, 5]:
|
for value in @[3, 4, 5]:
|
||||||
echo value
|
echo value
|
||||||
# --> 3
|
# --> 3
|
||||||
|
|
@ -1308,6 +1353,7 @@ with a compatible base type can be passed to an openarray parameter, the index
|
||||||
type does not matter.
|
type does not matter.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
|
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
|
||||||
capitals: array[3, string] # array of strings with a fixed size
|
capitals: array[3, string] # array of strings with a fixed size
|
||||||
|
|
@ -1337,6 +1383,7 @@ arguments to a procedure. The compiler converts the list of arguments
|
||||||
to an array automatically:
|
to an array automatically:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc myWriteln(f: File, a: varargs[string]) =
|
proc myWriteln(f: File, a: varargs[string]) =
|
||||||
for s in items(a):
|
for s in items(a):
|
||||||
write(f, s)
|
write(f, s)
|
||||||
|
|
@ -1351,6 +1398,7 @@ last parameter in the procedure header. It is also possible to perform
|
||||||
type conversions in this context:
|
type conversions in this context:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc myWriteln(f: File, a: varargs[string, `$`]) =
|
proc myWriteln(f: File, a: varargs[string, `$`]) =
|
||||||
for s in items(a):
|
for s in items(a):
|
||||||
write(f, s)
|
write(f, s)
|
||||||
|
|
@ -1374,6 +1422,7 @@ context. A slice is just an object of type Slice which contains two bounds,
|
||||||
define operators which accept Slice objects to define ranges.
|
define operators which accept Slice objects to define ranges.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
var
|
var
|
||||||
a = "Nim is a progamming language"
|
a = "Nim is a progamming language"
|
||||||
|
|
@ -1388,27 +1437,31 @@ slice's bounds can hold any value supported by
|
||||||
their type, but it is the proc using the slice object which defines what values
|
their type, but it is the proc using the slice object which defines what values
|
||||||
are accepted.
|
are accepted.
|
||||||
|
|
||||||
To understand some of the different ways of specifying the indices of strings, arrays, sequences, etc.,
|
To understand some of the different ways of specifying the indices of
|
||||||
it must be remembered that Nim uses zero-based indices.
|
strings, arrays, sequences, etc., it must be remembered that Nim uses
|
||||||
|
zero-based indices.
|
||||||
|
|
||||||
So the string ``b`` is of length 19, and two different ways of specifying the indices are
|
So the string ``b`` is of length 19, and two different ways of specifying the
|
||||||
|
indices are
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
"Slices are useless."
|
"Slices are useless."
|
||||||
| | |
|
| | |
|
||||||
0 11 17 using indices
|
0 11 17 using indices
|
||||||
^19 ^8 ^2 using ^ syntax
|
^19 ^8 ^2 using ^ syntax
|
||||||
|
|
||||||
where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..<b.len]``, and it can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``
|
where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..<b.len]``, and it
|
||||||
|
can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``.
|
||||||
|
|
||||||
In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful"
|
In the above example, because the string ends in a period, to get the portion of the
|
||||||
|
string that is "useless" and replace it with "useful".
|
||||||
|
|
||||||
``b[11..^2]`` is the portion "useless", and
|
``b[11..^2]`` is the portion "useless", and ``b[11..^2] = "useful"`` replaces the
|
||||||
``b[11..^2] = "useful"`` replaces the "useless" portion with "useful",
|
"useless" portion with "useful", giving the result "Slices are useful."
|
||||||
giving the result "Slices are useful."
|
|
||||||
|
|
||||||
Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or as ``b[11..b.len-2] = "useful"`` or as ``b[11..<b.len-1] = "useful"`` or as ....
|
Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or
|
||||||
|
as ``b[11..b.len-2] = "useful"`` or as ``b[11..<b.len-1] = "useful"``.
|
||||||
|
|
||||||
Tuples
|
Tuples
|
||||||
------
|
------
|
||||||
|
|
@ -1425,6 +1478,7 @@ The assignment operator for tuples copies each component. The notation
|
||||||
integer.
|
integer.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Person = tuple[name: string, age: int] # type representing a person:
|
Person = tuple[name: string, age: int] # type representing a person:
|
||||||
|
|
@ -1470,6 +1524,7 @@ otherwise you will be assigning the same value to all the individual
|
||||||
variables! For example:
|
variables! For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
@ -1509,6 +1564,7 @@ tuple/object field operator) and ``[]`` (array/string/sequence index operator)
|
||||||
operators perform implicit dereferencing operations for reference types:
|
operators perform implicit dereferencing operations for reference types:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Node = ref object
|
Node = ref object
|
||||||
|
|
@ -1538,6 +1594,7 @@ techniques.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
proc echoItem(x: int) = echo x
|
proc echoItem(x: int) = echo x
|
||||||
|
|
||||||
proc forEach(action: proc (x: int)) =
|
proc forEach(action: proc (x: int)) =
|
||||||
|
|
@ -1555,9 +1612,11 @@ listed in the `manual <manual.html#types-procedural-type>`_.
|
||||||
|
|
||||||
Distinct type
|
Distinct type
|
||||||
-------------
|
-------------
|
||||||
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
|
A Distinct type allows for the creation of new type that "does not imply a
|
||||||
|
subtype relationship between it and its base type".
|
||||||
You must **explicitly** define all behaviour for the distinct type.
|
You must **explicitly** define all behaviour for the distinct type.
|
||||||
To help with this, both the distinct type and its base type can cast from one type to the other.
|
To help with this, both the distinct type and its base type can cast from one
|
||||||
|
type to the other.
|
||||||
Examples are provided in the `manual <manual.html#types-distinct-type>`_.
|
Examples are provided in the `manual <manual.html#types-distinct-type>`_.
|
||||||
|
|
||||||
Modules
|
Modules
|
||||||
|
|
@ -1592,39 +1651,6 @@ Each module has a special magic constant ``isMainModule`` that is true if the
|
||||||
module is compiled as the main file. This is very useful to embed tests within
|
module is compiled as the main file. This is very useful to embed tests within
|
||||||
the module as shown by the above example.
|
the module as shown by the above example.
|
||||||
|
|
||||||
Modules that depend on each other are possible, but strongly discouraged,
|
|
||||||
because then one module cannot be reused without the other.
|
|
||||||
|
|
||||||
The algorithm for compiling modules is:
|
|
||||||
|
|
||||||
- Compile the whole module as usual, following import statements recursively.
|
|
||||||
- If there is a cycle only import the already parsed symbols (that are
|
|
||||||
exported); if an unknown identifier occurs then abort.
|
|
||||||
|
|
||||||
This is best illustrated by an example:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
# Module A
|
|
||||||
type
|
|
||||||
T1* = int # Module A exports the type ``T1``
|
|
||||||
import B # the compiler starts parsing B
|
|
||||||
|
|
||||||
proc main() =
|
|
||||||
var i = p(3) # works because B has been parsed completely here
|
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
# Module B
|
|
||||||
import A # A is not parsed here! Only the already known symbols
|
|
||||||
# of A are imported.
|
|
||||||
|
|
||||||
proc p*(x: A.T1): A.T1 =
|
|
||||||
# this works because the compiler has already
|
|
||||||
# added T1 to A's interface symbol table
|
|
||||||
result = x + 1
|
|
||||||
|
|
||||||
|
|
||||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
|
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
|
||||||
a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
|
a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
|
||||||
if it is defined in two (or more) different modules and both modules are
|
if it is defined in two (or more) different modules and both modules are
|
||||||
|
|
|
||||||
52
doc/tut2.rst
52
doc/tut2.rst
|
|
@ -55,6 +55,7 @@ Objects have access to their type at runtime. There is an
|
||||||
``of`` operator that can be used to check the object's type:
|
``of`` operator that can be used to check the object's type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
Person = ref object of RootObj
|
Person = ref object of RootObj
|
||||||
name*: string # the * means that `name` is accessible from other modules
|
name*: string # the * means that `name` is accessible from other modules
|
||||||
|
|
@ -103,6 +104,7 @@ would require arbitrary symbol lookahead which slows down compilation.)
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
Node = ref object # a reference to an object with the following field:
|
Node = ref object # a reference to an object with the following field:
|
||||||
le, ri: Node # left and right subtrees
|
le, ri: Node # left and right subtrees
|
||||||
|
|
@ -144,6 +146,7 @@ variant types are needed.
|
||||||
An example:
|
An example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
# This is an example how an abstract syntax tree could be modelled in Nim
|
# This is an example how an abstract syntax tree could be modelled in Nim
|
||||||
type
|
type
|
||||||
|
|
@ -201,9 +204,11 @@ This method call syntax is not restricted to objects, it can be used
|
||||||
for any type:
|
for any type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
import strutils
|
||||||
|
|
||||||
echo "abc".len # is the same as echo len("abc")
|
echo "abc".len # is the same as echo len("abc")
|
||||||
echo "abc".toUpper()
|
echo "abc".toUpperAscii()
|
||||||
echo({'a', 'b', 'c'}.card)
|
echo({'a', 'b', 'c'}.card)
|
||||||
stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
|
stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
|
||||||
|
|
||||||
|
|
@ -213,6 +218,7 @@ postfix notation.)
|
||||||
So "pure object oriented" code is easy to write:
|
So "pure object oriented" code is easy to write:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
import strutils, sequtils
|
import strutils, sequtils
|
||||||
|
|
||||||
stdout.writeLine("Give a list of numbers (separated by spaces): ")
|
stdout.writeLine("Give a list of numbers (separated by spaces): ")
|
||||||
|
|
@ -228,6 +234,7 @@ the same. But setting a value is different; for this a special setter syntax
|
||||||
is needed:
|
is needed:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Socket* = ref object of RootObj
|
Socket* = ref object of RootObj
|
||||||
|
|
@ -252,6 +259,7 @@ The ``[]`` array access operator can be overloaded to provide
|
||||||
`array properties`:idx:\ :
|
`array properties`:idx:\ :
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
Vector* = object
|
Vector* = object
|
||||||
x, y, z: float
|
x, y, z: float
|
||||||
|
|
@ -283,23 +291,24 @@ Procedures always use static dispatch. For dynamic dispatch replace the
|
||||||
``proc`` keyword by ``method``:
|
``proc`` keyword by ``method``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
PExpr = ref object of RootObj ## abstract base class for an expression
|
Expression = ref object of RootObj ## abstract base class for an expression
|
||||||
PLiteral = ref object of PExpr
|
Literal = ref object of Expression
|
||||||
x: int
|
x: int
|
||||||
PPlusExpr = ref object of PExpr
|
PlusExpr = ref object of Expression
|
||||||
a, b: PExpr
|
a, b: Expression
|
||||||
|
|
||||||
# watch out: 'eval' relies on dynamic binding
|
# watch out: 'eval' relies on dynamic binding
|
||||||
method eval(e: PExpr): int =
|
method eval(e: Expression): int =
|
||||||
# override this base method
|
# override this base method
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
method eval(e: PLiteral): int = e.x
|
method eval(e: Literal): int = e.x
|
||||||
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
|
method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
|
||||||
|
|
||||||
proc newLit(x: int): PLiteral = PLiteral(x: x)
|
proc newLit(x: int): Literal = Literal(x: x)
|
||||||
proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b)
|
proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
|
||||||
|
|
||||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||||
|
|
||||||
|
|
@ -311,6 +320,7 @@ In a multi-method all parameters that have an object type are used for the
|
||||||
dispatching:
|
dispatching:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
type
|
type
|
||||||
Thing = ref object of RootObj
|
Thing = ref object of RootObj
|
||||||
|
|
@ -365,6 +375,7 @@ Raise statement
|
||||||
Raising an exception is done with the ``raise`` statement:
|
Raising an exception is done with the ``raise`` statement:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
var
|
var
|
||||||
e: ref OSError
|
e: ref OSError
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -385,6 +396,9 @@ Try statement
|
||||||
The ``try`` statement handles exceptions:
|
The ``try`` statement handles exceptions:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
from strutils import parseInt
|
||||||
|
|
||||||
# read the first two lines of a text file that should contain numbers
|
# read the first two lines of a text file that should contain numbers
|
||||||
# and tries to add them
|
# and tries to add them
|
||||||
var
|
var
|
||||||
|
|
@ -479,6 +493,7 @@ with `type parameters`:idx:. They are most useful for efficient type safe
|
||||||
containers:
|
containers:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
type
|
type
|
||||||
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
||||||
# generic param ``T``
|
# generic param ``T``
|
||||||
|
|
@ -573,6 +588,7 @@ Templates are especially useful for lazy evaluation purposes. Consider a
|
||||||
simple proc for logging:
|
simple proc for logging:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
const
|
const
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|
@ -590,6 +606,7 @@ evaluation for procedures is *eager*).
|
||||||
Turning the ``log`` proc into a template solves this problem:
|
Turning the ``log`` proc into a template solves this problem:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
const
|
const
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|
@ -611,6 +628,7 @@ If the template has no explicit return type,
|
||||||
To pass a block of statements to a template, use 'untyped' for the last parameter:
|
To pass a block of statements to a template, use 'untyped' for the last parameter:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
template withFile(f: untyped, filename: string, mode: FileMode,
|
template withFile(f: untyped, filename: string, mode: FileMode,
|
||||||
body: untyped): typed =
|
body: untyped): typed =
|
||||||
|
|
@ -665,6 +683,7 @@ The following example implements a powerful ``debug`` command that accepts a
|
||||||
variable number of arguments:
|
variable number of arguments:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
# to work with Nim syntax trees, we need an API that is defined in the
|
# to work with Nim syntax trees, we need an API that is defined in the
|
||||||
# ``macros`` module:
|
# ``macros`` module:
|
||||||
import macros
|
import macros
|
||||||
|
|
@ -744,6 +763,7 @@ dynamic code into something that compiles statically. For the exercise we will
|
||||||
use the following snippet of code as the starting point:
|
use the following snippet of code as the starting point:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
|
|
||||||
import strutils, tables
|
import strutils, tables
|
||||||
|
|
||||||
|
|
@ -863,9 +883,9 @@ variables with ``cfg``. In essence, what the compiler is doing is replacing
|
||||||
the line calling the macro with the following snippet of code:
|
the line calling the macro with the following snippet of code:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
const cfgversion= "1.1"
|
const cfgversion = "1.1"
|
||||||
const cfglicenseOwner= "Hyori Lee"
|
const cfglicenseOwner = "Hyori Lee"
|
||||||
const cfglicenseKey= "M1Tl3PjBWO2CC48m"
|
const cfglicenseKey = "M1Tl3PjBWO2CC48m"
|
||||||
|
|
||||||
You can verify this yourself adding the line ``echo source`` somewhere at the
|
You can verify this yourself adding the line ``echo source`` somewhere at the
|
||||||
end of the macro and compiling the program. Another difference is that instead
|
end of the macro and compiling the program. Another difference is that instead
|
||||||
|
|
@ -891,12 +911,13 @@ an expression macro. Since we know that we want to generate a bunch of
|
||||||
see what the compiler *expects* from us:
|
see what the compiler *expects* from us:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
dumpTree:
|
dumpTree:
|
||||||
const cfgversion: string = "1.1"
|
const cfgversion: string = "1.1"
|
||||||
const cfglicenseOwner= "Hyori Lee"
|
const cfglicenseOwner = "Hyori Lee"
|
||||||
const cfglicenseKey= "M1Tl3PjBWO2CC48m"
|
const cfglicenseKey = "M1Tl3PjBWO2CC48m"
|
||||||
|
|
||||||
During compilation of the source code we should see the following lines in the
|
During compilation of the source code we should see the following lines in the
|
||||||
output (again, since this is a macro, compilation is enough, you don't have to
|
output (again, since this is a macro, compilation is enough, you don't have to
|
||||||
|
|
@ -996,6 +1017,7 @@ Lifting Procs
|
||||||
+++++++++++++
|
+++++++++++++
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
:test: "nim c $1"
|
||||||
import math
|
import math
|
||||||
|
|
||||||
template liftScalarProc(fname) =
|
template liftScalarProc(fname) =
|
||||||
|
|
|
||||||
|
|
@ -1226,3 +1226,8 @@ when not defined(booting):
|
||||||
macro payload: untyped {.gensym.} =
|
macro payload: untyped {.gensym.} =
|
||||||
result = parseStmt(e)
|
result = parseStmt(e)
|
||||||
payload()
|
payload()
|
||||||
|
|
||||||
|
macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
|
||||||
|
result = newCall(callee)
|
||||||
|
for i in 0 ..< args.len:
|
||||||
|
result.add args[i]
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,6 @@ type
|
||||||
RegexError* = object of ValueError
|
RegexError* = object of ValueError
|
||||||
## is raised if the pattern is no valid regular expression.
|
## is raised if the pattern is no valid regular expression.
|
||||||
|
|
||||||
{.deprecated: [TRegexFlag: RegexFlag, TRegexDesc: RegexDesc, TRegex: Regex,
|
|
||||||
EInvalidRegEx: RegexError].}
|
|
||||||
|
|
||||||
proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
|
proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
|
||||||
var e: ref RegexError
|
var e: ref RegexError
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -470,8 +467,8 @@ proc replacef*(s: string, sub: Regex, by: string): string =
|
||||||
prev = match.last + 1
|
prev = match.last + 1
|
||||||
add(result, substr(s, prev))
|
add(result, substr(s, prev))
|
||||||
|
|
||||||
proc parallelReplace*(s: string, subs: openArray[
|
proc multiReplace*(s: string, subs: openArray[
|
||||||
tuple[pattern: Regex, repl: string]]): string =
|
tuple[pattern: Regex, repl: string]]): string =
|
||||||
## Returns a modified copy of ``s`` with the substitutions in ``subs``
|
## Returns a modified copy of ``s`` with the substitutions in ``subs``
|
||||||
## applied in parallel.
|
## applied in parallel.
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -490,13 +487,20 @@ proc parallelReplace*(s: string, subs: openArray[
|
||||||
# copy the rest:
|
# copy the rest:
|
||||||
add(result, substr(s, i))
|
add(result, substr(s, i))
|
||||||
|
|
||||||
|
proc parallelReplace*(s: string, subs: openArray[
|
||||||
|
tuple[pattern: Regex, repl: string]]): string {.deprecated.} =
|
||||||
|
## Returns a modified copy of ``s`` with the substitutions in ``subs``
|
||||||
|
## applied in parallel.
|
||||||
|
## **Deprecated since version 0.18.0**: Use ``multiReplace`` instead.
|
||||||
|
result = multiReplace(s, subs)
|
||||||
|
|
||||||
proc transformFile*(infile, outfile: string,
|
proc transformFile*(infile, outfile: string,
|
||||||
subs: openArray[tuple[pattern: Regex, repl: string]]) =
|
subs: openArray[tuple[pattern: Regex, repl: string]]) =
|
||||||
## reads in the file ``infile``, performs a parallel replacement (calls
|
## reads in the file ``infile``, performs a parallel replacement (calls
|
||||||
## ``parallelReplace``) and writes back to ``outfile``. Raises ``IOError`` if an
|
## ``parallelReplace``) and writes back to ``outfile``. Raises ``IOError`` if an
|
||||||
## error occurs. This is supposed to be used for quick scripting.
|
## error occurs. This is supposed to be used for quick scripting.
|
||||||
var x = readFile(infile).string
|
var x = readFile(infile).string
|
||||||
writeFile(outfile, x.parallelReplace(subs))
|
writeFile(outfile, x.multiReplace(subs))
|
||||||
|
|
||||||
iterator split*(s: string, sep: Regex): string =
|
iterator split*(s: string, sep: Regex): string =
|
||||||
## Splits the string ``s`` into substrings.
|
## Splits the string ``s`` into substrings.
|
||||||
|
|
@ -579,12 +583,12 @@ const ## common regular expressions
|
||||||
## describes an URL
|
## describes an URL
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
doAssert match("(a b c)", re"\( .* \)")
|
doAssert match("(a b c)", rex"\( .* \)")
|
||||||
doAssert match("WHiLe", re("while", {reIgnoreCase}))
|
doAssert match("WHiLe", re("while", {reIgnoreCase}))
|
||||||
|
|
||||||
doAssert "0158787".match(re"\d+")
|
doAssert "0158787".match(re"\d+")
|
||||||
doAssert "ABC 0232".match(re"\w+\s+\d+")
|
doAssert "ABC 0232".match(re"\w+\s+\d+")
|
||||||
doAssert "ABC".match(re"\d+ | \w+")
|
doAssert "ABC".match(rex"\d+ | \w+")
|
||||||
|
|
||||||
{.push warnings:off.}
|
{.push warnings:off.}
|
||||||
doAssert matchLen("key", re(reIdentifier)) == 3
|
doAssert matchLen("key", re(reIdentifier)) == 3
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .}
|
||||||
## and not strings or numbers, this is a *comparison of references*.
|
## and not strings or numbers, this is a *comparison of references*.
|
||||||
|
|
||||||
{. experimental .}
|
{. experimental .}
|
||||||
macro `.`*(obj: JsObject, field: static[cstring]): JsObject =
|
macro `.`*(obj: JsObject, field: untyped): JsObject =
|
||||||
## Experimental dot accessor (get) for type JsObject.
|
## Experimental dot accessor (get) for type JsObject.
|
||||||
## Returns the value of a property of name `field` from a JsObject `x`.
|
## Returns the value of a property of name `field` from a JsObject `x`.
|
||||||
##
|
##
|
||||||
|
|
@ -196,14 +196,14 @@ macro `.`*(obj: JsObject, field: static[cstring]): JsObject =
|
||||||
helper(`obj`)
|
helper(`obj`)
|
||||||
else:
|
else:
|
||||||
if not mangledNames.hasKey($field):
|
if not mangledNames.hasKey($field):
|
||||||
mangledNames[$field] = $mangleJsName(field)
|
mangledNames[$field] = $mangleJsName($field)
|
||||||
let importString = "#." & mangledNames[$field]
|
let importString = "#." & mangledNames[$field]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
proc helper(o: JsObject): JsObject
|
proc helper(o: JsObject): JsObject
|
||||||
{. importcpp: `importString`, gensym .}
|
{. importcpp: `importString`, gensym .}
|
||||||
helper(`obj`)
|
helper(`obj`)
|
||||||
|
|
||||||
macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
|
macro `.=`*(obj: JsObject, field, value: untyped): untyped =
|
||||||
## Experimental dot accessor (set) for type JsObject.
|
## Experimental dot accessor (set) for type JsObject.
|
||||||
## Sets the value of a property of name `field` in a JsObject `x` to `value`.
|
## Sets the value of a property of name `field` in a JsObject `x` to `value`.
|
||||||
if validJsName($field):
|
if validJsName($field):
|
||||||
|
|
@ -214,7 +214,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
|
||||||
helper(`obj`, `value`)
|
helper(`obj`, `value`)
|
||||||
else:
|
else:
|
||||||
if not mangledNames.hasKey($field):
|
if not mangledNames.hasKey($field):
|
||||||
mangledNames[$field] = $mangleJsName(field)
|
mangledNames[$field] = $mangleJsName($field)
|
||||||
let importString = "#." & mangledNames[$field] & " = #"
|
let importString = "#." & mangledNames[$field] & " = #"
|
||||||
result = quote do:
|
result = quote do:
|
||||||
proc helper(o: JsObject, v: auto)
|
proc helper(o: JsObject, v: auto)
|
||||||
|
|
@ -222,7 +222,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
|
||||||
helper(`obj`, `value`)
|
helper(`obj`, `value`)
|
||||||
|
|
||||||
macro `.()`*(obj: JsObject,
|
macro `.()`*(obj: JsObject,
|
||||||
field: static[cstring],
|
field: untyped,
|
||||||
args: varargs[JsObject, jsFromAst]): JsObject =
|
args: varargs[JsObject, jsFromAst]): JsObject =
|
||||||
## Experimental "method call" operator for type JsObject.
|
## Experimental "method call" operator for type JsObject.
|
||||||
## Takes the name of a method of the JavaScript object (`field`) and calls
|
## Takes the name of a method of the JavaScript object (`field`) and calls
|
||||||
|
|
@ -245,7 +245,7 @@ macro `.()`*(obj: JsObject,
|
||||||
importString = "#." & $field & "(@)"
|
importString = "#." & $field & "(@)"
|
||||||
else:
|
else:
|
||||||
if not mangledNames.hasKey($field):
|
if not mangledNames.hasKey($field):
|
||||||
mangledNames[$field] = $mangleJsName(field)
|
mangledNames[$field] = $mangleJsName($field)
|
||||||
importString = "#." & mangledNames[$field] & "(@)"
|
importString = "#." & mangledNames[$field] & "(@)"
|
||||||
result = quote:
|
result = quote:
|
||||||
proc helper(o: JsObject): JsObject
|
proc helper(o: JsObject): JsObject
|
||||||
|
|
@ -257,7 +257,7 @@ macro `.()`*(obj: JsObject,
|
||||||
result[1].add args[idx].copyNimTree
|
result[1].add args[idx].copyNimTree
|
||||||
|
|
||||||
macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
field: static[cstring]): V =
|
field: untyped): V =
|
||||||
## Experimental dot accessor (get) for type JsAssoc.
|
## Experimental dot accessor (get) for type JsAssoc.
|
||||||
## Returns the value of a property of name `field` from a JsObject `x`.
|
## Returns the value of a property of name `field` from a JsObject `x`.
|
||||||
var importString: string
|
var importString: string
|
||||||
|
|
@ -265,7 +265,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
importString = "#." & $field
|
importString = "#." & $field
|
||||||
else:
|
else:
|
||||||
if not mangledNames.hasKey($field):
|
if not mangledNames.hasKey($field):
|
||||||
mangledNames[$field] = $mangleJsName(field)
|
mangledNames[$field] = $mangleJsName($field)
|
||||||
importString = "#." & mangledNames[$field]
|
importString = "#." & mangledNames[$field]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
proc helper(o: type(`obj`)): `obj`.V
|
proc helper(o: type(`obj`)): `obj`.V
|
||||||
|
|
@ -273,7 +273,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
helper(`obj`)
|
helper(`obj`)
|
||||||
|
|
||||||
macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
field: static[cstring],
|
field: untyped,
|
||||||
value: V): untyped =
|
value: V): untyped =
|
||||||
## Experimental dot accessor (set) for type JsAssoc.
|
## Experimental dot accessor (set) for type JsAssoc.
|
||||||
## Sets the value of a property of name `field` in a JsObject `x` to `value`.
|
## Sets the value of a property of name `field` in a JsObject `x` to `value`.
|
||||||
|
|
@ -282,7 +282,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
importString = "#." & $field & " = #"
|
importString = "#." & $field & " = #"
|
||||||
else:
|
else:
|
||||||
if not mangledNames.hasKey($field):
|
if not mangledNames.hasKey($field):
|
||||||
mangledNames[$field] = $mangleJsName(field)
|
mangledNames[$field] = $mangleJsName($field)
|
||||||
importString = "#." & mangledNames[$field] & " = #"
|
importString = "#." & mangledNames[$field] & " = #"
|
||||||
result = quote do:
|
result = quote do:
|
||||||
proc helper(o: type(`obj`), v: `obj`.V)
|
proc helper(o: type(`obj`), v: `obj`.V)
|
||||||
|
|
@ -290,7 +290,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
|
||||||
helper(`obj`, `value`)
|
helper(`obj`, `value`)
|
||||||
|
|
||||||
macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
|
macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
|
||||||
field: static[cstring],
|
field: untyped,
|
||||||
args: varargs[untyped]): auto =
|
args: varargs[untyped]): auto =
|
||||||
## Experimental "method call" operator for type JsAssoc.
|
## Experimental "method call" operator for type JsAssoc.
|
||||||
## Takes the name of a method of the JavaScript object (`field`) and calls
|
## Takes the name of a method of the JavaScript object (`field`) and calls
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ __clang__
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309)
|
# pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309)
|
||||||
# pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706)
|
# pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706)
|
||||||
# pragma warning(disable: 4710 4711 4774 4800 4820 4996 4090)
|
# pragma warning(disable: 4710 4711 4774 4800 4820 4996 4090 4297)
|
||||||
#endif
|
#endif
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,12 @@ type
|
||||||
state: TokenClass
|
state: TokenClass
|
||||||
|
|
||||||
SourceLanguage* = enum
|
SourceLanguage* = enum
|
||||||
langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava,
|
langNone, langNim, langCpp, langCsharp, langC, langJava,
|
||||||
langYaml
|
langYaml
|
||||||
{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
|
|
||||||
TGeneralTokenizer: GeneralTokenizer].}
|
|
||||||
|
|
||||||
const
|
const
|
||||||
sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
|
sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
|
||||||
"Nim", "Nimrod", "C++", "C#", "C", "Java", "Yaml"]
|
"Nim", "C++", "C#", "C", "Java", "Yaml"]
|
||||||
tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
|
tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
|
||||||
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
||||||
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
||||||
|
|
@ -398,7 +396,6 @@ type
|
||||||
TokenizerFlag = enum
|
TokenizerFlag = enum
|
||||||
hasPreprocessor, hasNestedComments
|
hasPreprocessor, hasNestedComments
|
||||||
TokenizerFlags = set[TokenizerFlag]
|
TokenizerFlags = set[TokenizerFlag]
|
||||||
{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
|
|
||||||
|
|
||||||
proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
|
proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
|
||||||
flags: TokenizerFlags) =
|
flags: TokenizerFlags) =
|
||||||
|
|
@ -888,7 +885,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
|
||||||
proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
|
proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
|
||||||
case lang
|
case lang
|
||||||
of langNone: assert false
|
of langNone: assert false
|
||||||
of langNim, langNimrod: nimNextToken(g)
|
of langNim: nimNextToken(g)
|
||||||
of langCpp: cppNextToken(g)
|
of langCpp: cppNextToken(g)
|
||||||
of langCsharp: csharpNextToken(g)
|
of langCsharp: csharpNextToken(g)
|
||||||
of langC: cNextToken(g)
|
of langC: cNextToken(g)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,6 @@ type
|
||||||
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
||||||
arg: string) {.nimcall.} ## what to do in case of an error
|
arg: string) {.nimcall.} ## what to do in case of an error
|
||||||
FindFileHandler* = proc (filename: string): string {.nimcall.}
|
FindFileHandler* = proc (filename: string): string {.nimcall.}
|
||||||
{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
|
|
||||||
TMsgKind: MsgKind].}
|
|
||||||
|
|
||||||
const
|
const
|
||||||
messages: array[MsgKind, string] = [
|
messages: array[MsgKind, string] = [
|
||||||
|
|
@ -127,8 +125,6 @@ type
|
||||||
bufpos*: int
|
bufpos*: int
|
||||||
line*, col*, baseIndent*: int
|
line*, col*, baseIndent*: int
|
||||||
skipPounds*: bool
|
skipPounds*: bool
|
||||||
{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
|
|
||||||
TLexer: Lexer].}
|
|
||||||
|
|
||||||
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
|
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
|
||||||
tok.kind = tkWord
|
tok.kind = tkWord
|
||||||
|
|
@ -288,10 +284,6 @@ type
|
||||||
hasToc*: bool
|
hasToc*: bool
|
||||||
|
|
||||||
EParseError* = object of ValueError
|
EParseError* = object of ValueError
|
||||||
{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
|
|
||||||
TSharedState: SharedState, TRstParser: RstParser,
|
|
||||||
TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
|
|
||||||
TMsgClass: MsgClass].}
|
|
||||||
|
|
||||||
proc whichMsgClass*(k: MsgKind): MsgClass =
|
proc whichMsgClass*(k: MsgKind): MsgClass =
|
||||||
## returns which message class `k` belongs to.
|
## returns which message class `k` belongs to.
|
||||||
|
|
@ -341,11 +333,6 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) =
|
||||||
p.col + p.tok[p.idx].col, msgKind,
|
p.col + p.tok[p.idx].col, msgKind,
|
||||||
p.tok[p.idx].symbol)
|
p.tok[p.idx].symbol)
|
||||||
|
|
||||||
when false:
|
|
||||||
proc corrupt(p: RstParser) =
|
|
||||||
assert p.indentStack[0] == 0
|
|
||||||
for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
|
|
||||||
|
|
||||||
proc currInd(p: RstParser): int =
|
proc currInd(p: RstParser): int =
|
||||||
result = p.indentStack[high(p.indentStack)]
|
result = p.indentStack[high(p.indentStack)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ type
|
||||||
target*: OutputTarget
|
target*: OutputTarget
|
||||||
config*: StringTableRef
|
config*: StringTableRef
|
||||||
splitAfter*: int # split too long entries in the TOC
|
splitAfter*: int # split too long entries in the TOC
|
||||||
listingCounter: int
|
listingCounter*: int
|
||||||
tocPart*: seq[TocEntry]
|
tocPart*: seq[TocEntry]
|
||||||
hasToc*: bool
|
hasToc*: bool
|
||||||
theIndex: string # Contents of the index file to be dumped at the end.
|
theIndex: string # Contents of the index file to be dumped at the end.
|
||||||
|
|
@ -61,6 +61,9 @@ type
|
||||||
seenIndexTerms: Table[string, int] ## \
|
seenIndexTerms: Table[string, int] ## \
|
||||||
## Keeps count of same text index terms to generate different identifiers
|
## Keeps count of same text index terms to generate different identifiers
|
||||||
## for hyperlinks. See renderIndexTerm proc for details.
|
## for hyperlinks. See renderIndexTerm proc for details.
|
||||||
|
id*: int ## A counter useful for generating IDs.
|
||||||
|
onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int;
|
||||||
|
content: string)
|
||||||
|
|
||||||
PDoc = var RstGenerator ## Alias to type less.
|
PDoc = var RstGenerator ## Alias to type less.
|
||||||
|
|
||||||
|
|
@ -69,8 +72,9 @@ type
|
||||||
startLine: int ## The starting line of the code block, by default 1.
|
startLine: int ## The starting line of the code block, by default 1.
|
||||||
langStr: string ## Input string used to specify the language.
|
langStr: string ## Input string used to specify the language.
|
||||||
lang: SourceLanguage ## Type of highlighting, by default none.
|
lang: SourceLanguage ## Type of highlighting, by default none.
|
||||||
{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
|
filename: string
|
||||||
TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
|
testCmd: string
|
||||||
|
status: int
|
||||||
|
|
||||||
proc init(p: var CodeBlockParams) =
|
proc init(p: var CodeBlockParams) =
|
||||||
## Default initialisation of CodeBlockParams to sane values.
|
## Default initialisation of CodeBlockParams to sane values.
|
||||||
|
|
@ -133,6 +137,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||||
g.options = options
|
g.options = options
|
||||||
g.findFile = findFile
|
g.findFile = findFile
|
||||||
g.currentSection = ""
|
g.currentSection = ""
|
||||||
|
g.id = 0
|
||||||
let fileParts = filename.splitFile
|
let fileParts = filename.splitFile
|
||||||
if fileParts.ext == ".nim":
|
if fileParts.ext == ".nim":
|
||||||
g.currentSection = "Module " & fileParts.name
|
g.currentSection = "Module " & fileParts.name
|
||||||
|
|
@ -368,7 +373,6 @@ type
|
||||||
##
|
##
|
||||||
## The value indexed by this IndexEntry is a sequence with the real index
|
## The value indexed by this IndexEntry is a sequence with the real index
|
||||||
## entries found in the ``.idx`` file.
|
## entries found in the ``.idx`` file.
|
||||||
{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
|
|
||||||
|
|
||||||
proc cmp(a, b: IndexEntry): int =
|
proc cmp(a, b: IndexEntry): int =
|
||||||
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
|
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
|
||||||
|
|
@ -823,13 +827,20 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
|
||||||
var number: int
|
var number: int
|
||||||
if parseInt(n.getFieldValue, number) > 0:
|
if parseInt(n.getFieldValue, number) > 0:
|
||||||
params.startLine = number
|
params.startLine = number
|
||||||
of "file":
|
of "file", "filename":
|
||||||
# The ``file`` option is a Nim extension to the official spec, it acts
|
# The ``file`` option is a Nim extension to the official spec, it acts
|
||||||
# like it would for other directives like ``raw`` or ``cvs-table``. This
|
# like it would for other directives like ``raw`` or ``cvs-table``. This
|
||||||
# field is dealt with in ``rst.nim`` which replaces the existing block with
|
# field is dealt with in ``rst.nim`` which replaces the existing block with
|
||||||
# the referenced file, so we only need to ignore it here to avoid incorrect
|
# the referenced file, so we only need to ignore it here to avoid incorrect
|
||||||
# warning messages.
|
# warning messages.
|
||||||
discard
|
params.filename = n.getFieldValue.strip
|
||||||
|
of "test":
|
||||||
|
params.testCmd = n.getFieldValue.strip
|
||||||
|
if params.testCmd.len == 0: params.testCmd = "nim c -r $1"
|
||||||
|
of "status":
|
||||||
|
var status: int
|
||||||
|
if parseInt(n.getFieldValue, status) > 0:
|
||||||
|
params.status = status
|
||||||
of "default-language":
|
of "default-language":
|
||||||
params.langStr = n.getFieldValue.strip
|
params.langStr = n.getFieldValue.strip
|
||||||
params.lang = params.langStr.getSourceLanguage
|
params.lang = params.langStr.getSourceLanguage
|
||||||
|
|
@ -901,6 +912,9 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var m = n.sons[2].sons[0]
|
var m = n.sons[2].sons[0]
|
||||||
assert m.kind == rnLeaf
|
assert m.kind == rnLeaf
|
||||||
|
|
||||||
|
if params.testCmd.len > 0 and d.onTestSnippet != nil:
|
||||||
|
d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text)
|
||||||
|
|
||||||
let (blockStart, blockEnd) = buildLinesHTMLTable(d, params, m.text)
|
let (blockStart, blockEnd) = buildLinesHTMLTable(d, params, m.text)
|
||||||
|
|
||||||
dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
|
dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
|
||||||
|
|
|
||||||
|
|
@ -59,9 +59,10 @@ export asyncfutures, asyncstreams
|
||||||
##
|
##
|
||||||
## .. code-block::nim
|
## .. code-block::nim
|
||||||
## var future = socket.recv(100)
|
## var future = socket.recv(100)
|
||||||
## future.callback =
|
## future.addCallback(
|
||||||
## proc () =
|
## proc () =
|
||||||
## echo(future.read)
|
## echo(future.read)
|
||||||
|
## )
|
||||||
##
|
##
|
||||||
## All asynchronous functions returning a ``Future`` will not block. They
|
## All asynchronous functions returning a ``Future`` will not block. They
|
||||||
## will not however return immediately. An asynchronous function will have
|
## will not however return immediately. An asynchronous function will have
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ proc all*[T](futs: varargs[Future[T]]): auto =
|
||||||
let totalFutures = len(futs)
|
let totalFutures = len(futs)
|
||||||
|
|
||||||
for fut in futs:
|
for fut in futs:
|
||||||
fut.callback = proc(f: Future[T]) =
|
fut.addCallback proc (f: Future[T]) =
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
if f.failed:
|
if f.failed:
|
||||||
|
|
@ -356,7 +356,7 @@ proc all*[T](futs: varargs[Future[T]]): auto =
|
||||||
|
|
||||||
for i, fut in futs:
|
for i, fut in futs:
|
||||||
proc setCallback(i: int) =
|
proc setCallback(i: int) =
|
||||||
fut.callback = proc(f: Future[T]) =
|
fut.addCallback proc (f: Future[T]) =
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
if f.failed:
|
if f.failed:
|
||||||
|
|
|
||||||
|
|
@ -275,10 +275,7 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
|
||||||
lineFut.mget() = newStringOfCap(80)
|
lineFut.mget() = newStringOfCap(80)
|
||||||
|
|
||||||
while not client.isClosed:
|
while not client.isClosed:
|
||||||
try:
|
await processRequest(server, request, client, address, lineFut, callback)
|
||||||
await processRequest(server, request, client, address, lineFut, callback)
|
|
||||||
except:
|
|
||||||
asyncCheck request.mget().respondError(Http500)
|
|
||||||
|
|
||||||
proc serve*(server: AsyncHttpServer, port: Port,
|
proc serve*(server: AsyncHttpServer, port: Port,
|
||||||
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
|
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ elif useICC_builtins:
|
||||||
|
|
||||||
|
|
||||||
proc countSetBits*(x: SomeInteger): int {.inline, nosideeffect.} =
|
proc countSetBits*(x: SomeInteger): int {.inline, nosideeffect.} =
|
||||||
## Counts the set bits in integer. (also called Hamming weight.)
|
## Counts the set bits in integer. (also called `Hamming weight`:idx:.)
|
||||||
# TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
|
# TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
|
||||||
# like GCC and MSVC
|
# like GCC and MSVC
|
||||||
when nimvm:
|
when nimvm:
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,8 @@
|
||||||
## writeLine(stdout, "your password: " & myData["password"])
|
## writeLine(stdout, "your password: " & myData["password"])
|
||||||
## writeLine(stdout, "</body></html>")
|
## writeLine(stdout, "</body></html>")
|
||||||
|
|
||||||
import strutils, os, strtabs, cookies
|
import strutils, os, strtabs, cookies, uri
|
||||||
|
export uri.encodeUrl, uri.decodeUrl
|
||||||
proc encodeUrl*(s: string): string =
|
|
||||||
## Encodes a value to be HTTP safe: This means that characters in the set
|
|
||||||
## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
|
|
||||||
## a space is converted to ``'+'`` and every other character is encoded as
|
|
||||||
## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
|
|
||||||
result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
|
|
||||||
for i in 0..s.len-1:
|
|
||||||
case s[i]
|
|
||||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
|
|
||||||
of ' ': add(result, '+')
|
|
||||||
else:
|
|
||||||
add(result, '%')
|
|
||||||
add(result, toHex(ord(s[i]), 2))
|
|
||||||
|
|
||||||
proc handleHexChar(c: char, x: var int) {.inline.} =
|
proc handleHexChar(c: char, x: var int) {.inline.} =
|
||||||
case c
|
case c
|
||||||
|
|
@ -52,28 +39,6 @@ proc handleHexChar(c: char, x: var int) {.inline.} =
|
||||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
|
|
||||||
proc decodeUrl*(s: string): string =
|
|
||||||
## Decodes a value from its HTTP representation: This means that a ``'+'``
|
|
||||||
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
|
|
||||||
## value) is converted to the character with ordinal number ``xx``, and
|
|
||||||
## and every other character is carried over.
|
|
||||||
result = newString(s.len)
|
|
||||||
var i = 0
|
|
||||||
var j = 0
|
|
||||||
while i < s.len:
|
|
||||||
case s[i]
|
|
||||||
of '%':
|
|
||||||
var x = 0
|
|
||||||
handleHexChar(s[i+1], x)
|
|
||||||
handleHexChar(s[i+2], x)
|
|
||||||
inc(i, 2)
|
|
||||||
result[j] = chr(x)
|
|
||||||
of '+': result[j] = ' '
|
|
||||||
else: result[j] = s[i]
|
|
||||||
inc(i)
|
|
||||||
inc(j)
|
|
||||||
setLen(result, j)
|
|
||||||
|
|
||||||
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
||||||
case c
|
case c
|
||||||
of '&': add(dest, "&")
|
of '&': add(dest, "&")
|
||||||
|
|
@ -390,8 +355,3 @@ proc existsCookie*(name: string): bool =
|
||||||
## Checks if a cookie of `name` exists.
|
## Checks if a cookie of `name` exists.
|
||||||
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
|
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
|
||||||
result = hasKey(gcookies, name)
|
result = hasKey(gcookies, name)
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
const test1 = "abc\L+def xyz"
|
|
||||||
assert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
|
|
||||||
assert decodeUrl(encodeUrl(test1)) == test1
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:
|
## .. code-block::
|
||||||
##
|
##
|
||||||
## let
|
## let
|
||||||
## s = @[1, 2, 3]
|
## s = @[1, 2, 3]
|
||||||
|
|
@ -84,7 +84,7 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:
|
## .. code-block::
|
||||||
##
|
##
|
||||||
## let
|
## let
|
||||||
## total = repeat(5, 3)
|
## total = repeat(5, 3)
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "",
|
||||||
|
|
||||||
# copy the attributes; when iterating over them these lists
|
# copy the attributes; when iterating over them these lists
|
||||||
# will be modified, so that each attribute is only given one value
|
# will be modified, so that each attribute is only given one value
|
||||||
var req = split(reqAttr)
|
var req = splitWhitespace(reqAttr)
|
||||||
var opt = split(optAttr)
|
var opt = splitWhitespace(optAttr)
|
||||||
result = newNimNode(nnkBracket, e)
|
result = newNimNode(nnkBracket, e)
|
||||||
result.add(newStrLitNode("<"))
|
result.add(newStrLitNode("<"))
|
||||||
result.add(newStrLitNode(tag))
|
result.add(newStrLitNode(tag))
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,9 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
|
||||||
inc(s.count)
|
inc(s.count)
|
||||||
pkey.events = events
|
pkey.events = events
|
||||||
|
|
||||||
proc unregister*[T](s: Selector[T], fd: int | SocketHandle) =
|
proc unregister*[T](s: Selector[T], fd: SocketHandle|int) =
|
||||||
s.withSelectLock():
|
s.withSelectLock():
|
||||||
|
let fd = fd.SocketHandle
|
||||||
var pkey = s.getKey(fd)
|
var pkey = s.getKey(fd)
|
||||||
if Event.Read in pkey.events:
|
if Event.Read in pkey.events:
|
||||||
IOFD_CLR(fd, addr s.rSet)
|
IOFD_CLR(fd, addr s.rSet)
|
||||||
|
|
@ -438,18 +439,19 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
|
||||||
body1, body2: untyped) =
|
body1, body2: untyped) =
|
||||||
mixin withSelectLock
|
mixin withSelectLock
|
||||||
s.withSelectLock():
|
s.withSelectLock():
|
||||||
var value: ptr T
|
block:
|
||||||
let fdi = int(fd)
|
var value: ptr T
|
||||||
var i = 0
|
let fdi = int(fd)
|
||||||
while i < FD_SETSIZE:
|
var i = 0
|
||||||
if s.fds[i].ident == fdi:
|
while i < FD_SETSIZE:
|
||||||
value = addr(s.fds[i].data)
|
if s.fds[i].ident == fdi:
|
||||||
break
|
value = addr(s.fds[i].data)
|
||||||
inc(i)
|
break
|
||||||
if i != FD_SETSIZE:
|
inc(i)
|
||||||
body1
|
if i != FD_SETSIZE:
|
||||||
else:
|
body1
|
||||||
body2
|
else:
|
||||||
|
body2
|
||||||
|
|
||||||
|
|
||||||
proc getFd*[T](s: Selector[T]): int =
|
proc getFd*[T](s: Selector[T]): int =
|
||||||
|
|
|
||||||
|
|
@ -1346,6 +1346,16 @@ proc createJsonIndexer(jsonNode: NimNode,
|
||||||
indexNode
|
indexNode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc transformJsonIndexer(jsonNode: NimNode): NimNode =
|
||||||
|
case jsonNode.kind
|
||||||
|
of nnkBracketExpr:
|
||||||
|
result = newNimNode(nnkCurlyExpr)
|
||||||
|
else:
|
||||||
|
result = jsonNode.copy()
|
||||||
|
|
||||||
|
for child in jsonNode:
|
||||||
|
result.add(transformJsonIndexer(child))
|
||||||
|
|
||||||
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
||||||
ast: string) =
|
ast: string) =
|
||||||
if node.kind notin kinds:
|
if node.kind notin kinds:
|
||||||
|
|
@ -1524,6 +1534,35 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||||
|
|
||||||
doAssert result.len > 0
|
doAssert result.len > 0
|
||||||
|
|
||||||
|
proc processFields(obj: NimNode,
|
||||||
|
jsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||||
|
## Process all the fields of an ``ObjectTy`` and any of its
|
||||||
|
## parent type's fields (via inheritance).
|
||||||
|
result = @[]
|
||||||
|
case obj.kind
|
||||||
|
of nnkObjectTy:
|
||||||
|
expectKind(obj[2], nnkRecList)
|
||||||
|
for field in obj[2]:
|
||||||
|
let nodes = processObjField(field, jsonNode)
|
||||||
|
result.add(nodes)
|
||||||
|
|
||||||
|
# process parent type fields
|
||||||
|
case obj[1].kind
|
||||||
|
of nnkBracketExpr:
|
||||||
|
assert $obj[1][0] == "ref"
|
||||||
|
result.add(processFields(getType(obj[1][1]), jsonNode))
|
||||||
|
of nnkSym:
|
||||||
|
result.add(processFields(getType(obj[1]), jsonNode))
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
of nnkTupleTy:
|
||||||
|
for identDefs in obj:
|
||||||
|
expectKind(identDefs, nnkIdentDefs)
|
||||||
|
let nodes = processObjField(identDefs[0], jsonNode)
|
||||||
|
result.add(nodes)
|
||||||
|
else:
|
||||||
|
doAssert false, "Unable to process field type: " & $obj.kind
|
||||||
|
|
||||||
proc processType(typeName: NimNode, obj: NimNode,
|
proc processType(typeName: NimNode, obj: NimNode,
|
||||||
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
|
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
|
||||||
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
||||||
|
|
@ -1533,20 +1572,21 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
## .. code-block::plain
|
## .. code-block::plain
|
||||||
## ObjectTy
|
## ObjectTy
|
||||||
## Empty
|
## Empty
|
||||||
## Empty
|
## InheritanceInformation
|
||||||
## RecList
|
## RecList
|
||||||
## Sym "events"
|
## Sym "events"
|
||||||
case obj.kind
|
case obj.kind
|
||||||
of nnkObjectTy:
|
of nnkObjectTy, nnkTupleTy:
|
||||||
# Create object constructor.
|
# Create object constructor.
|
||||||
result = newNimNode(nnkObjConstr)
|
result =
|
||||||
result.add(typeName) # Name of the type to construct.
|
if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
|
||||||
|
else: newNimNode(nnkPar)
|
||||||
|
|
||||||
# Process each object field and add it as an exprColonExpr
|
if obj.kind == nnkObjectTy:
|
||||||
expectKind(obj[2], nnkRecList)
|
result.add(typeName) # Name of the type to construct.
|
||||||
for field in obj[2]:
|
|
||||||
let nodes = processObjField(field, jsonNode)
|
# Process each object/tuple field and add it as an exprColonExpr
|
||||||
result.add(nodes)
|
result.add(processFields(obj, jsonNode))
|
||||||
|
|
||||||
# Object might be null. So we need to check for that.
|
# Object might be null. So we need to check for that.
|
||||||
if isRef:
|
if isRef:
|
||||||
|
|
@ -1569,25 +1609,14 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
`getEnumCall`
|
`getEnumCall`
|
||||||
)
|
)
|
||||||
of nnkSym:
|
of nnkSym:
|
||||||
case ($typeName).normalize
|
let name = ($typeName).normalize
|
||||||
of "float":
|
case name
|
||||||
result = quote do:
|
|
||||||
(
|
|
||||||
verifyJsonKind(`jsonNode`, {JFloat, JInt}, astToStr(`jsonNode`));
|
|
||||||
if `jsonNode`.kind == JFloat: `jsonNode`.fnum else: `jsonNode`.num.float
|
|
||||||
)
|
|
||||||
of "string":
|
of "string":
|
||||||
result = quote do:
|
result = quote do:
|
||||||
(
|
(
|
||||||
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
|
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
|
||||||
if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
|
if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
|
||||||
)
|
)
|
||||||
of "int":
|
|
||||||
result = quote do:
|
|
||||||
(
|
|
||||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
|
||||||
`jsonNode`.num.int
|
|
||||||
)
|
|
||||||
of "biggestint":
|
of "biggestint":
|
||||||
result = quote do:
|
result = quote do:
|
||||||
(
|
(
|
||||||
|
|
@ -1601,12 +1630,36 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
`jsonNode`.bval
|
`jsonNode`.bval
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
doAssert false, "Unable to process nnkSym " & $typeName
|
if name.startsWith("int") or name.startsWith("uint"):
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||||
|
`jsonNode`.num.`obj`
|
||||||
|
)
|
||||||
|
elif name.startsWith("float"):
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
|
||||||
|
if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
doAssert false, "Unable to process nnkSym " & $typeName
|
||||||
else:
|
else:
|
||||||
doAssert false, "Unable to process type: " & $obj.kind
|
doAssert false, "Unable to process type: " & $obj.kind
|
||||||
|
|
||||||
doAssert(not result.isNil(), "processType not initialised.")
|
doAssert(not result.isNil(), "processType not initialised.")
|
||||||
|
|
||||||
|
import options
|
||||||
|
proc workaroundMacroNone[T](): Option[T] =
|
||||||
|
none(T)
|
||||||
|
|
||||||
|
proc depth(n: NimNode, current = 0): int =
|
||||||
|
result = 1
|
||||||
|
for child in n:
|
||||||
|
let d = 1 + child.depth(current + 1)
|
||||||
|
if d > result:
|
||||||
|
result = d
|
||||||
|
|
||||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
||||||
##
|
##
|
||||||
|
|
@ -1616,10 +1669,50 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
# echo("--createConsuctor-- \n", treeRepr(typeSym))
|
# echo("--createConsuctor-- \n", treeRepr(typeSym))
|
||||||
# echo()
|
# echo()
|
||||||
|
|
||||||
|
if depth(jsonNode) > 150:
|
||||||
|
error("The `to` macro does not support ref objects with cycles.", jsonNode)
|
||||||
|
|
||||||
case typeSym.kind
|
case typeSym.kind
|
||||||
of nnkBracketExpr:
|
of nnkBracketExpr:
|
||||||
var bracketName = ($typeSym[0]).normalize
|
var bracketName = ($typeSym[0]).normalize
|
||||||
case bracketName
|
case bracketName
|
||||||
|
of "option":
|
||||||
|
# TODO: Would be good to verify that this is Option[T] from
|
||||||
|
# options module I suppose.
|
||||||
|
let lenientJsonNode = transformJsonIndexer(jsonNode)
|
||||||
|
|
||||||
|
let optionGeneric = typeSym[1]
|
||||||
|
let value = createConstructor(typeSym[1], jsonNode)
|
||||||
|
let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
|
||||||
|
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
if `lenientJsonNode`.isNil: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
|
||||||
|
)
|
||||||
|
of "table", "orderedtable":
|
||||||
|
let tableKeyType = typeSym[1]
|
||||||
|
if ($tableKeyType).cmpIgnoreStyle("string") != 0:
|
||||||
|
error("JSON doesn't support keys of type " & $tableKeyType)
|
||||||
|
let tableValueType = typeSym[2]
|
||||||
|
|
||||||
|
let forLoopKey = genSym(nskForVar, "key")
|
||||||
|
let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
|
||||||
|
let constructorNode = createConstructor(tableValueType, indexerNode)
|
||||||
|
|
||||||
|
let tableInit =
|
||||||
|
if bracketName == "table":
|
||||||
|
bindSym("initTable")
|
||||||
|
else:
|
||||||
|
bindSym("initOrderedTable")
|
||||||
|
|
||||||
|
# Create a statement expression containing a for loop.
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
var map = `tableInit`[`tableKeyType`, `tableValueType`]();
|
||||||
|
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||||
|
for `forLoopKey` in keys(`jsonNode`.fields): map[`forLoopKey`] = `constructorNode`;
|
||||||
|
map
|
||||||
|
)
|
||||||
of "ref":
|
of "ref":
|
||||||
# Ref type.
|
# Ref type.
|
||||||
var typeName = $typeSym[1]
|
var typeName = $typeSym[1]
|
||||||
|
|
@ -1663,12 +1756,23 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
let obj = getType(typeSym)
|
let obj = getType(typeSym)
|
||||||
result = processType(typeSym, obj, jsonNode, false)
|
result = processType(typeSym, obj, jsonNode, false)
|
||||||
of nnkSym:
|
of nnkSym:
|
||||||
|
# Handle JsonNode.
|
||||||
|
if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
|
||||||
|
return jsonNode
|
||||||
|
|
||||||
|
# Handle all other types.
|
||||||
let obj = getType(typeSym)
|
let obj = getType(typeSym)
|
||||||
if obj.kind == nnkBracketExpr:
|
if obj.kind == nnkBracketExpr:
|
||||||
# When `Sym "Foo"` turns out to be a `ref object`.
|
# When `Sym "Foo"` turns out to be a `ref object`.
|
||||||
result = createConstructor(obj, jsonNode)
|
result = createConstructor(obj, jsonNode)
|
||||||
else:
|
else:
|
||||||
result = processType(typeSym, obj, jsonNode, false)
|
result = processType(typeSym, obj, jsonNode, false)
|
||||||
|
of nnkTupleTy:
|
||||||
|
result = processType(typeSym, typeSym, jsonNode, false)
|
||||||
|
of nnkPar:
|
||||||
|
# TODO: The fact that `jsonNode` here works to give a good line number
|
||||||
|
# is weird. Specifying typeSym should work but doesn't.
|
||||||
|
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
|
||||||
else:
|
else:
|
||||||
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
||||||
|
|
||||||
|
|
@ -1796,10 +1900,18 @@ macro to*(node: JsonNode, T: typedesc): untyped =
|
||||||
expectKind(typeNode, nnkBracketExpr)
|
expectKind(typeNode, nnkBracketExpr)
|
||||||
doAssert(($typeNode[0]).normalize == "typedesc")
|
doAssert(($typeNode[0]).normalize == "typedesc")
|
||||||
|
|
||||||
result = createConstructor(typeNode[1], node)
|
# Create `temp` variable to store the result in case the user calls this
|
||||||
# TODO: Rename postProcessValue and move it (?)
|
# on `parseJson` (see bug #6604).
|
||||||
result = postProcessValue(result)
|
result = newNimNode(nnkStmtListExpr)
|
||||||
|
let temp = genSym(nskLet, "temp")
|
||||||
|
result.add quote do:
|
||||||
|
let `temp` = `node`
|
||||||
|
|
||||||
|
let constructor = createConstructor(typeNode[1], temp)
|
||||||
|
# TODO: Rename postProcessValue and move it (?)
|
||||||
|
result.add(postProcessValue(constructor))
|
||||||
|
|
||||||
|
# echo(treeRepr(result))
|
||||||
# echo(toStrLit(result))
|
# echo(toStrLit(result))
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
|
|
|
||||||
|
|
@ -202,13 +202,17 @@ when not defined(js):
|
||||||
|
|
||||||
proc countLogLines(logger: RollingFileLogger): int =
|
proc countLogLines(logger: RollingFileLogger): int =
|
||||||
result = 0
|
result = 0
|
||||||
for line in logger.file.lines():
|
let fp = open(logger.baseName, fmRead)
|
||||||
|
for line in fp.lines():
|
||||||
result.inc()
|
result.inc()
|
||||||
|
fp.close()
|
||||||
|
|
||||||
proc countFiles(filename: string): int =
|
proc countFiles(filename: string): int =
|
||||||
# Example: file.log.1
|
# Example: file.log.1
|
||||||
result = 0
|
result = 0
|
||||||
let (dir, name, ext) = splitFile(filename)
|
var (dir, name, ext) = splitFile(filename)
|
||||||
|
if dir == "":
|
||||||
|
dir = "."
|
||||||
for kind, path in walkDir(dir):
|
for kind, path in walkDir(dir):
|
||||||
if kind == pcFile:
|
if kind == pcFile:
|
||||||
let llfn = name & ext & ExtSep
|
let llfn = name & ext & ExtSep
|
||||||
|
|
|
||||||
|
|
@ -491,6 +491,8 @@ const mimes* = {
|
||||||
"vrml": "x-world/x-vrml",
|
"vrml": "x-world/x-vrml",
|
||||||
"wrl": "x-world/x-vrml"}
|
"wrl": "x-world/x-vrml"}
|
||||||
|
|
||||||
|
from strutils import startsWith
|
||||||
|
|
||||||
proc newMimetypes*(): MimeDB =
|
proc newMimetypes*(): MimeDB =
|
||||||
## Creates a new Mimetypes database. The database will contain the most
|
## Creates a new Mimetypes database. The database will contain the most
|
||||||
## common mimetypes.
|
## common mimetypes.
|
||||||
|
|
@ -498,8 +500,11 @@ proc newMimetypes*(): MimeDB =
|
||||||
|
|
||||||
proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
|
proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
|
||||||
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
|
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
|
||||||
## could not be found.
|
## could not be found. ``ext`` can start with an optional dot which is ignored.
|
||||||
result = mimedb.mimes.getOrDefault(ext)
|
if ext.startsWith("."):
|
||||||
|
result = mimedb.mimes.getOrDefault(ext.substr(1))
|
||||||
|
else:
|
||||||
|
result = mimedb.mimes.getOrDefault(ext)
|
||||||
if result == "":
|
if result == "":
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ type
|
||||||
|
|
||||||
SOBool* = enum ## Boolean socket options.
|
SOBool* = enum ## Boolean socket options.
|
||||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||||
OptOOBInline, OptReuseAddr, OptReusePort
|
OptOOBInline, OptReuseAddr, OptReusePort, OptNoDelay
|
||||||
|
|
||||||
ReadLineResult* = enum ## result for readLineAsync
|
ReadLineResult* = enum ## result for readLineAsync
|
||||||
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
||||||
|
|
@ -865,6 +865,11 @@ proc close*(socket: Socket) =
|
||||||
|
|
||||||
socket.fd.close()
|
socket.fd.close()
|
||||||
|
|
||||||
|
when defined(posix):
|
||||||
|
from posix import TCP_NODELAY
|
||||||
|
else:
|
||||||
|
from winlean import TCP_NODELAY
|
||||||
|
|
||||||
proc toCInt*(opt: SOBool): cint =
|
proc toCInt*(opt: SOBool): cint =
|
||||||
## Converts a ``SOBool`` into its Socket Option cint representation.
|
## Converts a ``SOBool`` into its Socket Option cint representation.
|
||||||
case opt
|
case opt
|
||||||
|
|
@ -876,6 +881,7 @@ proc toCInt*(opt: SOBool): cint =
|
||||||
of OptOOBInline: SO_OOBINLINE
|
of OptOOBInline: SO_OOBINLINE
|
||||||
of OptReuseAddr: SO_REUSEADDR
|
of OptReuseAddr: SO_REUSEADDR
|
||||||
of OptReusePort: SO_REUSEPORT
|
of OptReusePort: SO_REUSEPORT
|
||||||
|
of OptNoDelay: TCP_NODELAY
|
||||||
|
|
||||||
proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
|
proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
|
|
@ -898,6 +904,12 @@ proc getPeerAddr*(socket: Socket): (string, Port) =
|
||||||
proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
|
proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
|
||||||
tags: [WriteIOEffect].} =
|
tags: [WriteIOEffect].} =
|
||||||
## Sets option ``opt`` to a boolean value specified by ``value``.
|
## Sets option ``opt`` to a boolean value specified by ``value``.
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## var socket = newSocket()
|
||||||
|
## socket.setSockOpt(OptReusePort, true)
|
||||||
|
## socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
|
||||||
|
##
|
||||||
var valuei = cint(if value: 1 else: 0)
|
var valuei = cint(if value: 1 else: 0)
|
||||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -630,7 +630,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
## the process has finished. To execute a program without having a
|
## the process has finished. To execute a program without having a
|
||||||
## shell involved, use the `execProcess` proc of the `osproc`
|
## shell involved, use the `execProcess` proc of the `osproc`
|
||||||
## module.
|
## module.
|
||||||
when defined(linux):
|
when defined(posix):
|
||||||
result = c_system(command) shr 8
|
result = c_system(command) shr 8
|
||||||
else:
|
else:
|
||||||
result = c_system(command)
|
result = c_system(command)
|
||||||
|
|
|
||||||
|
|
@ -602,14 +602,13 @@ proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".}
|
||||||
else:
|
else:
|
||||||
return "'" & s.replace("'", "'\"'\"'") & "'"
|
return "'" & s.replace("'", "'\"'\"'") & "'"
|
||||||
|
|
||||||
proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
when defined(windows) or defined(posix):
|
||||||
## Quote ``s``, so it can be safely passed to shell.
|
proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||||
when defined(Windows):
|
## Quote ``s``, so it can be safely passed to shell.
|
||||||
return quoteShellWindows(s)
|
when defined(windows):
|
||||||
elif defined(posix):
|
return quoteShellWindows(s)
|
||||||
return quoteShellPosix(s)
|
else:
|
||||||
else:
|
return quoteShellPosix(s)
|
||||||
{.error:"quoteShell is not supported on your system".}
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
assert quoteShellWindows("aaa") == "aaa"
|
assert quoteShellWindows("aaa") == "aaa"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ type
|
||||||
## Windows: Named pipes are used so that you can peek
|
## Windows: Named pipes are used so that you can peek
|
||||||
## at the process' output streams.
|
## at the process' output streams.
|
||||||
poDemon ## Windows: The program creates no Window.
|
poDemon ## Windows: The program creates no Window.
|
||||||
|
## Unix: Start the program as a demon. This is still
|
||||||
|
## work in progress!
|
||||||
|
|
||||||
ProcessObj = object of RootObj
|
ProcessObj = object of RootObj
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -167,8 +169,7 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
|
||||||
## On posix, if the process has exited because of a signal, 128 + signal
|
## On posix, if the process has exited because of a signal, 128 + signal
|
||||||
## number will be returned.
|
## number will be returned.
|
||||||
|
|
||||||
|
proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
|
||||||
proc peekExitCode*(p: Process): int {.tags: [].}
|
|
||||||
## return -1 if the process is still running. Otherwise the process' exit code
|
## return -1 if the process is still running. Otherwise the process' exit code
|
||||||
##
|
##
|
||||||
## On posix, if the process has exited because of a signal, 128 + signal
|
## On posix, if the process has exited because of a signal, 128 + signal
|
||||||
|
|
@ -231,55 +232,79 @@ proc execProcesses*(cmds: openArray[string],
|
||||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||||
## that execute in parallel. The highest return value of all processes
|
## that execute in parallel. The highest return value of all processes
|
||||||
## is returned. Runs `beforeRunEvent` before running each command.
|
## is returned. Runs `beforeRunEvent` before running each command.
|
||||||
when false:
|
|
||||||
# poParentStreams causes problems on Posix, so we simply disable it:
|
|
||||||
var options = options - {poParentStreams}
|
|
||||||
|
|
||||||
assert n > 0
|
assert n > 0
|
||||||
if n > 1:
|
if n > 1:
|
||||||
var q: seq[Process]
|
var i = 0
|
||||||
newSeq(q, n)
|
var q = newSeq[Process](n)
|
||||||
var m = min(n, cmds.len)
|
var m = min(n, cmds.len)
|
||||||
for i in 0..m-1:
|
|
||||||
|
when defined(windows):
|
||||||
|
var w: WOHandleArray
|
||||||
|
var wcount = m
|
||||||
|
|
||||||
|
while i < m:
|
||||||
if beforeRunEvent != nil:
|
if beforeRunEvent != nil:
|
||||||
beforeRunEvent(i)
|
beforeRunEvent(i)
|
||||||
q[i] = startProcess(cmds[i], options=options + {poEvalCommand})
|
q[i] = startProcess(cmds[i], options = options + {poEvalCommand})
|
||||||
when defined(noBusyWaiting):
|
when defined(windows):
|
||||||
var r = 0
|
w[i] = q[i].fProcessHandle
|
||||||
for i in m..high(cmds):
|
inc(i)
|
||||||
when defined(debugExecProcesses):
|
|
||||||
var err = ""
|
var ecount = len(cmds)
|
||||||
var outp = outputStream(q[r])
|
while ecount > 0:
|
||||||
while running(q[r]) or not atEnd(outp):
|
when defined(windows):
|
||||||
err.add(outp.readLine())
|
# waiting for all children, get result if any child exits
|
||||||
err.add("\n")
|
var ret = waitForMultipleObjects(int32(wcount), addr(w), 0'i32,
|
||||||
echo(err)
|
INFINITE)
|
||||||
result = max(waitForExit(q[r]), result)
|
if ret == WAIT_TIMEOUT:
|
||||||
if afterRunEvent != nil: afterRunEvent(r, q[r])
|
# must not be happen
|
||||||
if q[r] != nil: close(q[r])
|
discard
|
||||||
if beforeRunEvent != nil:
|
elif ret == WAIT_FAILED:
|
||||||
beforeRunEvent(i)
|
raiseOSError(osLastError())
|
||||||
q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
|
else:
|
||||||
r = (r + 1) mod n
|
var status : cint = 1
|
||||||
else:
|
# waiting for all children, get result if any child exits
|
||||||
var i = m
|
let res = waitpid(-1, status, 0)
|
||||||
while i <= high(cmds):
|
if res > 0:
|
||||||
sleep(50)
|
for r in 0..m-1:
|
||||||
for r in 0..n-1:
|
if not isNil(q[r]) and q[r].id == res:
|
||||||
|
# we updating `exitStatus` manually, so `running()` can work.
|
||||||
|
if WIFEXITED(status) or WIFSIGNALED(status):
|
||||||
|
q[r].exitStatus = status
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
let err = osLastError()
|
||||||
|
if err == OSErrorCode(ECHILD):
|
||||||
|
# some child exits, we need to check our childs exit codes
|
||||||
|
discard
|
||||||
|
elif err == OSErrorCode(EINTR):
|
||||||
|
# signal interrupted our syscall, lets repeat it
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# all other errors are exceptions
|
||||||
|
raiseOSError(err)
|
||||||
|
|
||||||
|
for r in 0..m-1:
|
||||||
|
if not isNil(q[r]):
|
||||||
if not running(q[r]):
|
if not running(q[r]):
|
||||||
#echo(outputStream(q[r]).readLine())
|
result = max(result, q[r].peekExitCode())
|
||||||
result = max(waitForExit(q[r]), result)
|
|
||||||
if afterRunEvent != nil: afterRunEvent(r, q[r])
|
if afterRunEvent != nil: afterRunEvent(r, q[r])
|
||||||
if q[r] != nil: close(q[r])
|
close(q[r])
|
||||||
if beforeRunEvent != nil:
|
if i < len(cmds):
|
||||||
beforeRunEvent(i)
|
if beforeRunEvent != nil: beforeRunEvent(i)
|
||||||
q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
|
q[r] = startProcess(cmds[i],
|
||||||
inc(i)
|
options = options + {poEvalCommand})
|
||||||
if i > high(cmds): break
|
when defined(windows):
|
||||||
for j in 0..m-1:
|
w[r] = q[r].fProcessHandle
|
||||||
result = max(waitForExit(q[j]), result)
|
inc(i)
|
||||||
if afterRunEvent != nil: afterRunEvent(j, q[j])
|
else:
|
||||||
if q[j] != nil: close(q[j])
|
q[r] = nil
|
||||||
|
when defined(windows):
|
||||||
|
for c in r..MAXIMUM_WAIT_OBJECTS - 2:
|
||||||
|
w[c] = w[c + 1]
|
||||||
|
dec(wcount)
|
||||||
|
dec(ecount)
|
||||||
else:
|
else:
|
||||||
for i in 0..high(cmds):
|
for i in 0..high(cmds):
|
||||||
if beforeRunEvent != nil:
|
if beforeRunEvent != nil:
|
||||||
|
|
@ -321,6 +346,8 @@ when not defined(useNimRtl):
|
||||||
elif not running(p): break
|
elif not running(p): break
|
||||||
close(p)
|
close(p)
|
||||||
|
|
||||||
|
template streamAccess(p) =
|
||||||
|
assert poParentStreams notin p.options, "API usage error: stream access not allowed when you use poParentStreams"
|
||||||
|
|
||||||
when defined(Windows) and not defined(useNimRtl):
|
when defined(Windows) and not defined(useNimRtl):
|
||||||
# We need to implement a handle stream for Windows:
|
# We need to implement a handle stream for Windows:
|
||||||
|
|
@ -581,12 +608,15 @@ when defined(Windows) and not defined(useNimRtl):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
proc inputStream(p: Process): Stream =
|
proc inputStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
result = newFileHandleStream(p.inHandle)
|
result = newFileHandleStream(p.inHandle)
|
||||||
|
|
||||||
proc outputStream(p: Process): Stream =
|
proc outputStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
result = newFileHandleStream(p.outHandle)
|
result = newFileHandleStream(p.outHandle)
|
||||||
|
|
||||||
proc errorStream(p: Process): Stream =
|
proc errorStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
result = newFileHandleStream(p.errHandle)
|
result = newFileHandleStream(p.errHandle)
|
||||||
|
|
||||||
proc execCmd(command: string): int =
|
proc execCmd(command: string): int =
|
||||||
|
|
@ -682,9 +712,7 @@ elif not defined(useNimRtl):
|
||||||
sysEnv: cstringArray
|
sysEnv: cstringArray
|
||||||
workingDir: cstring
|
workingDir: cstring
|
||||||
pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
|
pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
|
||||||
optionPoUsePath: bool
|
options: set[ProcessOption]
|
||||||
optionPoParentStreams: bool
|
|
||||||
optionPoStdErrToStdOut: bool
|
|
||||||
{.deprecated: [TStartProcessData: StartProcessData].}
|
{.deprecated: [TStartProcessData: StartProcessData].}
|
||||||
|
|
||||||
const useProcessAuxSpawn = declared(posix_spawn) and not defined(useFork) and
|
const useProcessAuxSpawn = declared(posix_spawn) and not defined(useFork) and
|
||||||
|
|
@ -749,10 +777,8 @@ elif not defined(useNimRtl):
|
||||||
data.pStdin = pStdin
|
data.pStdin = pStdin
|
||||||
data.pStdout = pStdout
|
data.pStdout = pStdout
|
||||||
data.pStderr = pStderr
|
data.pStderr = pStderr
|
||||||
data.optionPoParentStreams = poParentStreams in options
|
|
||||||
data.optionPoUsePath = poUsePath in options
|
|
||||||
data.optionPoStdErrToStdOut = poStdErrToStdOut in options
|
|
||||||
data.workingDir = workingDir
|
data.workingDir = workingDir
|
||||||
|
data.options = options
|
||||||
|
|
||||||
when useProcessAuxSpawn:
|
when useProcessAuxSpawn:
|
||||||
var currentDir = getCurrentDir()
|
var currentDir = getCurrentDir()
|
||||||
|
|
@ -801,19 +827,22 @@ elif not defined(useNimRtl):
|
||||||
var mask: Sigset
|
var mask: Sigset
|
||||||
chck sigemptyset(mask)
|
chck sigemptyset(mask)
|
||||||
chck posix_spawnattr_setsigmask(attr, mask)
|
chck posix_spawnattr_setsigmask(attr, mask)
|
||||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
if poDemon in data.options:
|
||||||
|
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||||
|
|
||||||
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
var flags = POSIX_SPAWN_USEVFORK or
|
||||||
POSIX_SPAWN_SETSIGMASK or
|
POSIX_SPAWN_SETSIGMASK
|
||||||
POSIX_SPAWN_SETPGROUP)
|
if poDemon in data.options:
|
||||||
|
flags = flags or POSIX_SPAWN_SETPGROUP
|
||||||
|
chck posix_spawnattr_setflags(attr, flags)
|
||||||
|
|
||||||
if not data.optionPoParentStreams:
|
if not (poParentStreams in data.options):
|
||||||
chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
|
chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
|
||||||
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
|
||||||
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
|
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
|
||||||
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
|
||||||
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
|
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
|
||||||
if data.optionPoStdErrToStdOut:
|
if (poStdErrToStdOut in data.options):
|
||||||
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
|
||||||
else:
|
else:
|
||||||
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
|
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
|
||||||
|
|
@ -823,7 +852,7 @@ elif not defined(useNimRtl):
|
||||||
setCurrentDir($data.workingDir)
|
setCurrentDir($data.workingDir)
|
||||||
var pid: Pid
|
var pid: Pid
|
||||||
|
|
||||||
if data.optionPoUsePath:
|
if (poUsePath in data.options):
|
||||||
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
else:
|
else:
|
||||||
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
|
|
@ -885,7 +914,7 @@ elif not defined(useNimRtl):
|
||||||
# Warning: no GC here!
|
# Warning: no GC here!
|
||||||
# Or anything that touches global structures - all called nim procs
|
# Or anything that touches global structures - all called nim procs
|
||||||
# must be marked with stackTrace:off. Inspect C code after making changes.
|
# must be marked with stackTrace:off. Inspect C code after making changes.
|
||||||
if not data.optionPoParentStreams:
|
if not (poParentStreams in data.options):
|
||||||
discard close(data.pStdin[writeIdx])
|
discard close(data.pStdin[writeIdx])
|
||||||
if dup2(data.pStdin[readIdx], readIdx) < 0:
|
if dup2(data.pStdin[readIdx], readIdx) < 0:
|
||||||
startProcessFail(data)
|
startProcessFail(data)
|
||||||
|
|
@ -893,7 +922,7 @@ elif not defined(useNimRtl):
|
||||||
if dup2(data.pStdout[writeIdx], writeIdx) < 0:
|
if dup2(data.pStdout[writeIdx], writeIdx) < 0:
|
||||||
startProcessFail(data)
|
startProcessFail(data)
|
||||||
discard close(data.pStderr[readIdx])
|
discard close(data.pStderr[readIdx])
|
||||||
if data.optionPoStdErrToStdOut:
|
if (poStdErrToStdOut in data.options):
|
||||||
if dup2(data.pStdout[writeIdx], 2) < 0:
|
if dup2(data.pStdout[writeIdx], 2) < 0:
|
||||||
startProcessFail(data)
|
startProcessFail(data)
|
||||||
else:
|
else:
|
||||||
|
|
@ -907,7 +936,7 @@ elif not defined(useNimRtl):
|
||||||
discard close(data.pErrorPipe[readIdx])
|
discard close(data.pErrorPipe[readIdx])
|
||||||
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||||
|
|
||||||
if data.optionPoUsePath:
|
if (poUsePath in data.options):
|
||||||
when defined(uClibc) or defined(linux):
|
when defined(uClibc) or defined(linux):
|
||||||
# uClibc environment (OpenWrt included) doesn't have the full execvpe
|
# uClibc environment (OpenWrt included) doesn't have the full execvpe
|
||||||
let exe = findExe(data.sysCommand)
|
let exe = findExe(data.sysCommand)
|
||||||
|
|
@ -939,19 +968,22 @@ elif not defined(useNimRtl):
|
||||||
if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
|
if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
|
||||||
|
|
||||||
proc running(p: Process): bool =
|
proc running(p: Process): bool =
|
||||||
var ret : int
|
if p.exitStatus != -3:
|
||||||
var status : cint = 1
|
|
||||||
ret = waitpid(p.id, status, WNOHANG)
|
|
||||||
if ret == int(p.id):
|
|
||||||
if isExitStatus(status):
|
|
||||||
p.exitStatus = status
|
|
||||||
return false
|
|
||||||
else:
|
|
||||||
return true
|
|
||||||
elif ret == 0:
|
|
||||||
return true # Can't establish status. Assume running.
|
|
||||||
else:
|
|
||||||
return false
|
return false
|
||||||
|
else:
|
||||||
|
var ret : int
|
||||||
|
var status : cint = 1
|
||||||
|
ret = waitpid(p.id, status, WNOHANG)
|
||||||
|
if ret == int(p.id):
|
||||||
|
if isExitStatus(status):
|
||||||
|
p.exitStatus = status
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
elif ret == 0:
|
||||||
|
return true # Can't establish status. Assume running.
|
||||||
|
else:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc terminate(p: Process) =
|
proc terminate(p: Process) =
|
||||||
if kill(p.id, SIGTERM) != 0'i32:
|
if kill(p.id, SIGTERM) != 0'i32:
|
||||||
|
|
@ -1152,16 +1184,19 @@ elif not defined(useNimRtl):
|
||||||
stream = newFileStream(f)
|
stream = newFileStream(f)
|
||||||
|
|
||||||
proc inputStream(p: Process): Stream =
|
proc inputStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
if p.inStream == nil:
|
if p.inStream == nil:
|
||||||
createStream(p.inStream, p.inHandle, fmWrite)
|
createStream(p.inStream, p.inHandle, fmWrite)
|
||||||
return p.inStream
|
return p.inStream
|
||||||
|
|
||||||
proc outputStream(p: Process): Stream =
|
proc outputStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
if p.outStream == nil:
|
if p.outStream == nil:
|
||||||
createStream(p.outStream, p.outHandle, fmRead)
|
createStream(p.outStream, p.outHandle, fmRead)
|
||||||
return p.outStream
|
return p.outStream
|
||||||
|
|
||||||
proc errorStream(p: Process): Stream =
|
proc errorStream(p: Process): Stream =
|
||||||
|
streamAccess(p)
|
||||||
if p.errStream == nil:
|
if p.errStream == nil:
|
||||||
createStream(p.errStream, p.errHandle, fmRead)
|
createStream(p.errStream, p.errHandle, fmRead)
|
||||||
return p.errStream
|
return p.errStream
|
||||||
|
|
|
||||||
|
|
@ -956,6 +956,7 @@ proc parseInsert(p: var SqlParser): SqlNode =
|
||||||
if p.tok.kind == tkParLe:
|
if p.tok.kind == tkParLe:
|
||||||
var n = newNode(nkColumnList)
|
var n = newNode(nkColumnList)
|
||||||
parseParIdentList(p, n)
|
parseParIdentList(p, n)
|
||||||
|
result.add n
|
||||||
else:
|
else:
|
||||||
result.add(nil)
|
result.add(nil)
|
||||||
if isKeyw(p, "default"):
|
if isKeyw(p, "default"):
|
||||||
|
|
@ -1160,7 +1161,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||||
else:
|
else:
|
||||||
s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"")
|
s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"")
|
||||||
of nkStringLit:
|
of nkStringLit:
|
||||||
s.add(escape(n.strVal, "e'", "'"))
|
s.add(escape(n.strVal, "'", "'"))
|
||||||
of nkBitStringLit:
|
of nkBitStringLit:
|
||||||
s.add("b'" & n.strVal & "'")
|
s.add("b'" & n.strVal & "'")
|
||||||
of nkHexStringLit:
|
of nkHexStringLit:
|
||||||
|
|
@ -1240,7 +1241,7 @@ proc ra(n: SqlNode, s: var string, indent: int) =
|
||||||
if n.sons[2].kind == nkDefault:
|
if n.sons[2].kind == nkDefault:
|
||||||
s.add("default values")
|
s.add("default values")
|
||||||
else:
|
else:
|
||||||
s.add("\nvalues ")
|
s.add("\n")
|
||||||
ra(n.sons[2], s, indent)
|
ra(n.sons[2], s, indent)
|
||||||
s.add(';')
|
s.add(';')
|
||||||
of nkUpdate:
|
of nkUpdate:
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,8 @@ else:
|
||||||
include ioselects/ioselectors_select
|
include ioselects/ioselectors_select
|
||||||
elif defined(solaris):
|
elif defined(solaris):
|
||||||
include ioselects/ioselectors_poll # need to replace it with event ports
|
include ioselects/ioselectors_poll # need to replace it with event ports
|
||||||
|
elif defined(genode):
|
||||||
|
include ioselects/ioselectors_select # TODO: use the native VFS layer
|
||||||
else:
|
else:
|
||||||
include ioselects/ioselectors_poll
|
include ioselects/ioselectors_poll
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,6 @@ when defined(nimOldSplit):
|
||||||
else:
|
else:
|
||||||
{.pragma: deprecatedSplit.}
|
{.pragma: deprecatedSplit.}
|
||||||
|
|
||||||
type
|
|
||||||
CharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
|
||||||
{.deprecated: [TCharSet: CharSet].}
|
|
||||||
|
|
||||||
const
|
const
|
||||||
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||||
## All the characters that count as whitespace.
|
## All the characters that count as whitespace.
|
||||||
|
|
@ -78,40 +74,40 @@ proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
|
||||||
return c in Letters
|
return c in Letters
|
||||||
|
|
||||||
proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
|
proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsAlphaNumericChar".}=
|
rtl, extern: "nsuIsAlphaNumericChar".} =
|
||||||
## Checks whether or not `c` is alphanumeric.
|
## Checks whether or not `c` is alphanumeric.
|
||||||
##
|
##
|
||||||
## This checks a-z, A-Z, 0-9 ASCII characters only.
|
## This checks a-z, A-Z, 0-9 ASCII characters only.
|
||||||
return c in Letters or c in Digits
|
return c in Letters+Digits
|
||||||
|
|
||||||
proc isDigit*(c: char): bool {.noSideEffect, procvar,
|
proc isDigit*(c: char): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsDigitChar".}=
|
rtl, extern: "nsuIsDigitChar".} =
|
||||||
## Checks whether or not `c` is a number.
|
## Checks whether or not `c` is a number.
|
||||||
##
|
##
|
||||||
## This checks 0-9 ASCII characters only.
|
## This checks 0-9 ASCII characters only.
|
||||||
return c in Digits
|
return c in Digits
|
||||||
|
|
||||||
proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
|
proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsSpaceAsciiChar".}=
|
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 isLowerAscii*(c: char): bool {.noSideEffect, procvar,
|
proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsLowerAsciiChar".}=
|
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 isUpperAscii*(c: char): bool {.noSideEffect, procvar,
|
proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsUpperAsciiChar".}=
|
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 isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
|
proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsAlphaAsciiStr".}=
|
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.
|
||||||
|
|
@ -123,10 +119,10 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
|
|
||||||
result = true
|
result = true
|
||||||
for c in s:
|
for c in s:
|
||||||
result = c.isAlphaAscii() and result
|
if not c.isAlphaAscii(): return false
|
||||||
|
|
||||||
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsAlphaNumericStr".}=
|
rtl, extern: "nsuIsAlphaNumericStr".} =
|
||||||
## Checks whether or not `s` is alphanumeric.
|
## Checks whether or not `s` is alphanumeric.
|
||||||
##
|
##
|
||||||
## This checks a-z, A-Z, 0-9 ASCII characters only.
|
## This checks a-z, A-Z, 0-9 ASCII characters only.
|
||||||
|
|
@ -142,7 +138,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsDigitStr".}=
|
rtl, extern: "nsuIsDigitStr".} =
|
||||||
## Checks whether or not `s` is a numeric value.
|
## Checks whether or not `s` is a numeric value.
|
||||||
##
|
##
|
||||||
## This checks 0-9 ASCII characters only.
|
## This checks 0-9 ASCII characters only.
|
||||||
|
|
@ -158,7 +154,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsSpaceAsciiStr".}=
|
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
|
||||||
|
|
@ -172,7 +168,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
|
proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsLowerAsciiStr".}=
|
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.
|
||||||
|
|
@ -187,7 +183,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
true
|
true
|
||||||
|
|
||||||
proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
|
proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuIsUpperAsciiStr".}=
|
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.
|
||||||
|
|
@ -506,16 +502,15 @@ template splitCommon(s, sep, maxsplit, sepLen) =
|
||||||
var last = 0
|
var last = 0
|
||||||
var splits = maxsplit
|
var splits = maxsplit
|
||||||
|
|
||||||
if len(s) > 0:
|
while last <= len(s):
|
||||||
while last <= len(s):
|
var first = last
|
||||||
var first = last
|
while last < len(s) and not stringHasSep(s, last, sep):
|
||||||
while last < len(s) and not stringHasSep(s, last, sep):
|
inc(last)
|
||||||
inc(last)
|
if splits == 0: last = len(s)
|
||||||
if splits == 0: last = len(s)
|
yield substr(s, first, last-1)
|
||||||
yield substr(s, first, last-1)
|
if splits == 0: break
|
||||||
if splits == 0: break
|
dec(splits)
|
||||||
dec(splits)
|
inc(last, sepLen)
|
||||||
inc(last, sepLen)
|
|
||||||
|
|
||||||
template oldSplit(s, seps, maxsplit) =
|
template oldSplit(s, seps, maxsplit) =
|
||||||
var last = 0
|
var last = 0
|
||||||
|
|
@ -673,30 +668,29 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
|
||||||
splits = maxsplit
|
splits = maxsplit
|
||||||
startPos = 0
|
startPos = 0
|
||||||
|
|
||||||
if len(s) > 0:
|
# go to -1 in order to get separators at the beginning
|
||||||
# go to -1 in order to get separators at the beginning
|
while first >= -1:
|
||||||
while first >= -1:
|
while first >= 0 and not stringHasSep(s, first, sep):
|
||||||
while first >= 0 and not stringHasSep(s, first, sep):
|
|
||||||
dec(first)
|
|
||||||
|
|
||||||
if splits == 0:
|
|
||||||
# No more splits means set first to the beginning
|
|
||||||
first = -1
|
|
||||||
|
|
||||||
if first == -1:
|
|
||||||
startPos = 0
|
|
||||||
else:
|
|
||||||
startPos = first + sepLen
|
|
||||||
|
|
||||||
yield substr(s, startPos, last)
|
|
||||||
|
|
||||||
if splits == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
dec(splits)
|
|
||||||
dec(first)
|
dec(first)
|
||||||
|
|
||||||
last = first
|
if splits == 0:
|
||||||
|
# No more splits means set first to the beginning
|
||||||
|
first = -1
|
||||||
|
|
||||||
|
if first == -1:
|
||||||
|
startPos = 0
|
||||||
|
else:
|
||||||
|
startPos = first + sepLen
|
||||||
|
|
||||||
|
yield substr(s, startPos, last)
|
||||||
|
|
||||||
|
if splits == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
dec(splits)
|
||||||
|
dec(first)
|
||||||
|
|
||||||
|
last = first
|
||||||
|
|
||||||
iterator rsplit*(s: string, seps: set[char] = Whitespace,
|
iterator rsplit*(s: string, seps: set[char] = Whitespace,
|
||||||
maxsplit: int = -1): string =
|
maxsplit: int = -1): string =
|
||||||
|
|
@ -824,12 +818,18 @@ proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[st
|
||||||
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
|
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
|
||||||
## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
|
## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
|
||||||
## proc that returns a sequence of substrings.
|
## proc that returns a sequence of substrings.
|
||||||
|
runnableExamples:
|
||||||
|
doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
|
||||||
|
doAssert "".split({' '}) == @[""]
|
||||||
accumulateResult(split(s, seps, maxsplit))
|
accumulateResult(split(s, seps, maxsplit))
|
||||||
|
|
||||||
proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
|
proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||||
rtl, extern: "nsuSplitChar".} =
|
rtl, extern: "nsuSplitChar".} =
|
||||||
## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
|
## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
|
||||||
## that returns a sequence of substrings.
|
## that returns a sequence of substrings.
|
||||||
|
runnableExamples:
|
||||||
|
doAssert "a,b,c".split(',') == @["a", "b", "c"]
|
||||||
|
doAssert "".split(' ') == @[""]
|
||||||
accumulateResult(split(s, sep, maxsplit))
|
accumulateResult(split(s, sep, maxsplit))
|
||||||
|
|
||||||
proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
|
proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
|
||||||
|
|
@ -838,6 +838,13 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
|
||||||
##
|
##
|
||||||
## Substrings are separated by the string `sep`. This is a wrapper around the
|
## Substrings are separated by the string `sep`. This is a wrapper around the
|
||||||
## `split iterator <#split.i,string,string,int>`_.
|
## `split iterator <#split.i,string,string,int>`_.
|
||||||
|
runnableExamples:
|
||||||
|
doAssert "a,b,c".split(",") == @["a", "b", "c"]
|
||||||
|
doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"]
|
||||||
|
doAssert "".split("Elon Musk") == @[""]
|
||||||
|
doAssert "a largely spaced sentence".split(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
|
||||||
|
|
||||||
|
doAssert "a largely spaced sentence".split(" ", maxsplit=1) == @["a", " largely spaced sentence"]
|
||||||
doAssert(sep.len > 0)
|
doAssert(sep.len > 0)
|
||||||
|
|
||||||
accumulateResult(split(s, sep, maxsplit))
|
accumulateResult(split(s, sep, maxsplit))
|
||||||
|
|
@ -906,6 +913,13 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## @["Root#Object#Method", "Index"]
|
## @["Root#Object#Method", "Index"]
|
||||||
##
|
##
|
||||||
|
runnableExamples:
|
||||||
|
doAssert "a largely spaced sentence".rsplit(" ", maxsplit=1) == @["a largely spaced", "sentence"]
|
||||||
|
|
||||||
|
doAssert "a,b,c".rsplit(",") == @["a", "b", "c"]
|
||||||
|
doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ", "plan ", "canal panama"]
|
||||||
|
doAssert "".rsplit("Elon Musk") == @[""]
|
||||||
|
doAssert "a largely spaced sentence".rsplit(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
|
||||||
accumulateResult(rsplit(s, sep, maxsplit))
|
accumulateResult(rsplit(s, sep, maxsplit))
|
||||||
result.reverse()
|
result.reverse()
|
||||||
|
|
||||||
|
|
@ -1305,14 +1319,13 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
|
||||||
## This is often useful for generating some code where the items need to
|
## This is often useful for generating some code where the items need to
|
||||||
## be *separated* by `sep`. `sep` is only added if `dest` is longer than
|
## be *separated* by `sep`. `sep` is only added if `dest` is longer than
|
||||||
## `startLen`. The following example creates a string describing
|
## `startLen`. The following example creates a string describing
|
||||||
## an array of integers:
|
## an array of integers.
|
||||||
##
|
runnableExamples:
|
||||||
## .. code-block:: nim
|
var arr = "["
|
||||||
## var arr = "["
|
for x in items([2, 3, 5, 7, 11]):
|
||||||
## for x in items([2, 3, 5, 7, 11]):
|
addSep(arr, startLen=len("["))
|
||||||
## addSep(arr, startLen=len("["))
|
add(arr, $x)
|
||||||
## add(arr, $x)
|
add(arr, "]")
|
||||||
## add(arr, "]")
|
|
||||||
if dest.len > startLen: add(dest, sep)
|
if dest.len > startLen: add(dest, sep)
|
||||||
|
|
||||||
proc allCharsInSet*(s: string, theSet: set[char]): bool =
|
proc allCharsInSet*(s: string, theSet: set[char]): bool =
|
||||||
|
|
@ -1730,7 +1743,9 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
|
||||||
##
|
##
|
||||||
## Even though the algorithm works with any string `s`, it is only useful
|
## Even though the algorithm works with any string `s`, it is only useful
|
||||||
## if `s` contains a number.
|
## if `s` contains a number.
|
||||||
## Example: ``insertSep("1000000") == "1_000_000"``
|
runnableExamples:
|
||||||
|
doAssert insertSep("1000000") == "1_000_000"
|
||||||
|
|
||||||
var L = (s.len-1) div digits + s.len
|
var L = (s.len-1) div digits + s.len
|
||||||
result = newString(L)
|
result = newString(L)
|
||||||
var j = 0
|
var j = 0
|
||||||
|
|
@ -1818,6 +1833,8 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
|
||||||
##
|
##
|
||||||
## A valid identifier starts with a character of the set `IdentStartChars`
|
## A valid identifier starts with a character of the set `IdentStartChars`
|
||||||
## and is followed by any number of characters of the set `IdentChars`.
|
## and is followed by any number of characters of the set `IdentChars`.
|
||||||
|
runnableExamples:
|
||||||
|
doAssert "abc_def08".validIdentifier
|
||||||
if s[0] in IdentStartChars:
|
if s[0] in IdentStartChars:
|
||||||
for i in 1..s.len-1:
|
for i in 1..s.len-1:
|
||||||
if s[i] notin IdentChars: return false
|
if s[i] notin IdentChars: return false
|
||||||
|
|
@ -1828,7 +1845,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
|
||||||
## Returns the edit distance between `a` and `b`.
|
## Returns the edit distance between `a` and `b`.
|
||||||
##
|
##
|
||||||
## This uses the `Levenshtein`:idx: distance algorithm with only a linear
|
## This uses the `Levenshtein`:idx: distance algorithm with only a linear
|
||||||
## memory overhead. This implementation is highly optimized!
|
## memory overhead.
|
||||||
var len1 = a.len
|
var len1 = a.len
|
||||||
var len2 = b.len
|
var len2 = b.len
|
||||||
if len1 > len2:
|
if len1 > len2:
|
||||||
|
|
@ -2007,16 +2024,11 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
## after the decimal point for Nim's ``float`` type.
|
## after the decimal point for Nim's ``float`` type.
|
||||||
##
|
##
|
||||||
## If ``precision == -1``, it tries to format it nicely.
|
## If ``precision == -1``, it tries to format it nicely.
|
||||||
##
|
runnableExamples:
|
||||||
## Examples:
|
let x = 123.456
|
||||||
##
|
doAssert x.formatFloat() == "123.4560000000000"
|
||||||
## .. code-block:: nim
|
doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
|
||||||
##
|
doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
|
||||||
## let x = 123.456
|
|
||||||
## doAssert x.formatFloat() == "123.4560000000000"
|
|
||||||
## doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
|
|
||||||
## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
|
|
||||||
##
|
|
||||||
result = formatBiggestFloat(f, format, precision, decimalSep)
|
result = formatBiggestFloat(f, format, precision, decimalSep)
|
||||||
|
|
||||||
proc trimZeros*(x: var string) {.noSideEffect.} =
|
proc trimZeros*(x: var string) {.noSideEffect.} =
|
||||||
|
|
@ -2051,18 +2063,13 @@ proc formatSize*(bytes: int64,
|
||||||
##
|
##
|
||||||
## `includeSpace` can be set to true to include the (SI preferred) space
|
## `includeSpace` can be set to true to include the (SI preferred) space
|
||||||
## between the number and the unit (e.g. 1 KiB).
|
## between the number and the unit (e.g. 1 KiB).
|
||||||
##
|
runnableExamples:
|
||||||
## Examples:
|
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||||
##
|
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||||
## .. code-block:: nim
|
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
|
||||||
##
|
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
||||||
## formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
doAssert formatSize(4096) == "4KiB"
|
||||||
## formatSize((2.234*1024*1024).int) == "2.234MiB"
|
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
||||||
## formatSize(4096, includeSpace=true) == "4 KiB"
|
|
||||||
## formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
|
||||||
## formatSize(4096) == "4KiB"
|
|
||||||
## formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
|
||||||
##
|
|
||||||
const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
|
const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
|
||||||
const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
|
const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
|
||||||
var
|
var
|
||||||
|
|
@ -2156,7 +2163,7 @@ proc formatEng*(f: BiggestFloat,
|
||||||
## formatEng(4100, unit="V") == "4.1e3 V"
|
## formatEng(4100, unit="V") == "4.1e3 V"
|
||||||
## formatEng(4100, unit="") == "4.1e3 " # Space with unit=""
|
## formatEng(4100, unit="") == "4.1e3 " # Space with unit=""
|
||||||
##
|
##
|
||||||
## `decimalSep` is used as the decimal separator
|
## `decimalSep` is used as the decimal separator.
|
||||||
var
|
var
|
||||||
absolute: BiggestFloat
|
absolute: BiggestFloat
|
||||||
significand: BiggestFloat
|
significand: BiggestFloat
|
||||||
|
|
@ -2369,17 +2376,16 @@ proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
|
||||||
rtl, extern: "nsuRemoveSuffixCharSet".} =
|
rtl, extern: "nsuRemoveSuffixCharSet".} =
|
||||||
## Removes all characters from `chars` from the end of the string `s`
|
## Removes all characters from `chars` from the end of the string `s`
|
||||||
## (in-place).
|
## (in-place).
|
||||||
##
|
runnableExamples:
|
||||||
## .. code-block:: nim
|
var userInput = "Hello World!*~\r\n"
|
||||||
## var userInput = "Hello World!*~\r\n"
|
userInput.removeSuffix
|
||||||
## userInput.removeSuffix
|
doAssert userInput == "Hello World!*~"
|
||||||
## doAssert userInput == "Hello World!*~"
|
userInput.removeSuffix({'~', '*'})
|
||||||
## userInput.removeSuffix({'~', '*'})
|
doAssert userInput == "Hello World!"
|
||||||
## doAssert userInput == "Hello World!"
|
|
||||||
##
|
var otherInput = "Hello!?!"
|
||||||
## var otherInput = "Hello!?!"
|
otherInput.removeSuffix({'!', '?'})
|
||||||
## otherInput.removeSuffix({'!', '?'})
|
doAssert otherInput == "Hello"
|
||||||
## doAssert otherInput == "Hello"
|
|
||||||
if s.len == 0: return
|
if s.len == 0: return
|
||||||
var last = s.high
|
var last = s.high
|
||||||
while last > -1 and s[last] in chars: last -= 1
|
while last > -1 and s[last] in chars: last -= 1
|
||||||
|
|
@ -2390,24 +2396,23 @@ proc removeSuffix*(s: var string, c: char) {.
|
||||||
## Removes all occurrences of a single character (in-place) from the end
|
## Removes all occurrences of a single character (in-place) from the end
|
||||||
## of a string.
|
## of a string.
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
runnableExamples:
|
||||||
## var table = "users"
|
var table = "users"
|
||||||
## table.removeSuffix('s')
|
table.removeSuffix('s')
|
||||||
## doAssert table == "user"
|
doAssert table == "user"
|
||||||
##
|
|
||||||
## var dots = "Trailing dots......."
|
var dots = "Trailing dots......."
|
||||||
## dots.removeSuffix('.')
|
dots.removeSuffix('.')
|
||||||
## doAssert dots == "Trailing dots"
|
doAssert dots == "Trailing dots"
|
||||||
removeSuffix(s, chars = {c})
|
removeSuffix(s, chars = {c})
|
||||||
|
|
||||||
proc removeSuffix*(s: var string, suffix: string) {.
|
proc removeSuffix*(s: var string, suffix: string) {.
|
||||||
rtl, extern: "nsuRemoveSuffixString".} =
|
rtl, extern: "nsuRemoveSuffixString".} =
|
||||||
## Remove the first matching suffix (in-place) from a string.
|
## Remove the first matching suffix (in-place) from a string.
|
||||||
##
|
runnableExamples:
|
||||||
## .. code-block:: nim
|
var answers = "yeses"
|
||||||
## var answers = "yeses"
|
answers.removeSuffix("es")
|
||||||
## answers.removeSuffix("es")
|
doAssert answers == "yes"
|
||||||
## doAssert answers == "yes"
|
|
||||||
var newLen = s.len
|
var newLen = s.len
|
||||||
if s.endsWith(suffix):
|
if s.endsWith(suffix):
|
||||||
newLen -= len(suffix)
|
newLen -= len(suffix)
|
||||||
|
|
@ -2418,16 +2423,16 @@ proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
|
||||||
## Removes all characters from `chars` from the start of the string `s`
|
## Removes all characters from `chars` from the start of the string `s`
|
||||||
## (in-place).
|
## (in-place).
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
runnableExamples:
|
||||||
## var userInput = "\r\n*~Hello World!"
|
var userInput = "\r\n*~Hello World!"
|
||||||
## userInput.removePrefix
|
userInput.removePrefix
|
||||||
## doAssert userInput == "*~Hello World!"
|
doAssert userInput == "*~Hello World!"
|
||||||
## userInput.removePrefix({'~', '*'})
|
userInput.removePrefix({'~', '*'})
|
||||||
## doAssert userInput == "Hello World!"
|
doAssert userInput == "Hello World!"
|
||||||
##
|
|
||||||
## var otherInput = "?!?Hello!?!"
|
var otherInput = "?!?Hello!?!"
|
||||||
## otherInput.removePrefix({'!', '?'})
|
otherInput.removePrefix({'!', '?'})
|
||||||
## doAssert otherInput == "Hello!?!"
|
doAssert otherInput == "Hello!?!"
|
||||||
var start = 0
|
var start = 0
|
||||||
while start < s.len and s[start] in chars: start += 1
|
while start < s.len and s[start] in chars: start += 1
|
||||||
if start > 0: s.delete(0, start - 1)
|
if start > 0: s.delete(0, start - 1)
|
||||||
|
|
@ -2437,20 +2442,20 @@ proc removePrefix*(s: var string, c: char) {.
|
||||||
## Removes all occurrences of a single character (in-place) from the start
|
## Removes all occurrences of a single character (in-place) from the start
|
||||||
## of a string.
|
## of a string.
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
runnableExamples:
|
||||||
## var ident = "pControl"
|
var ident = "pControl"
|
||||||
## ident.removePrefix('p')
|
ident.removePrefix('p')
|
||||||
## doAssert ident == "Control"
|
doAssert ident == "Control"
|
||||||
removePrefix(s, chars = {c})
|
removePrefix(s, chars = {c})
|
||||||
|
|
||||||
proc removePrefix*(s: var string, prefix: string) {.
|
proc removePrefix*(s: var string, prefix: string) {.
|
||||||
rtl, extern: "nsuRemovePrefixString".} =
|
rtl, extern: "nsuRemovePrefixString".} =
|
||||||
## Remove the first matching prefix (in-place) from a string.
|
## Remove the first matching prefix (in-place) from a string.
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
runnableExamples:
|
||||||
## var answers = "yesyes"
|
var answers = "yesyes"
|
||||||
## answers.removePrefix("yes")
|
answers.removePrefix("yes")
|
||||||
## doAssert answers == "yes"
|
doAssert answers == "yes"
|
||||||
if s.startsWith(prefix):
|
if s.startsWith(prefix):
|
||||||
s.delete(0, prefix.len - 1)
|
s.delete(0, prefix.len - 1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,33 +293,33 @@ proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
|
||||||
if pos < 0:
|
if pos < 0:
|
||||||
let (o, rl) = runeReverseOffset(s, -pos)
|
let (o, rl) = runeReverseOffset(s, -pos)
|
||||||
if len >= rl:
|
if len >= rl:
|
||||||
result = s[o.. s.len-1]
|
result = s.substr(o, s.len-1)
|
||||||
elif len < 0:
|
elif len < 0:
|
||||||
let e = rl + len
|
let e = rl + len
|
||||||
if e < 0:
|
if e < 0:
|
||||||
result = ""
|
result = ""
|
||||||
else:
|
else:
|
||||||
result = s[o.. runeOffset(s, e-(rl+pos) , o)-1]
|
result = s.substr(o, runeOffset(s, e-(rl+pos) , o)-1)
|
||||||
else:
|
else:
|
||||||
result = s[o.. runeOffset(s, len, o)-1]
|
result = s.substr(o, runeOffset(s, len, o)-1)
|
||||||
else:
|
else:
|
||||||
let o = runeOffset(s, pos)
|
let o = runeOffset(s, pos)
|
||||||
if o < 0:
|
if o < 0:
|
||||||
result = ""
|
result = ""
|
||||||
elif len == int.high:
|
elif len == int.high:
|
||||||
result = s[o.. s.len-1]
|
result = s.substr(o, s.len-1)
|
||||||
elif len < 0:
|
elif len < 0:
|
||||||
let (e, rl) = runeReverseOffset(s, -len)
|
let (e, rl) = runeReverseOffset(s, -len)
|
||||||
discard rl
|
discard rl
|
||||||
if e <= 0:
|
if e <= 0:
|
||||||
result = ""
|
result = ""
|
||||||
else:
|
else:
|
||||||
result = s[o.. e-1]
|
result = s.substr(o, e-1)
|
||||||
else:
|
else:
|
||||||
var e = runeOffset(s, len, o)
|
var e = runeOffset(s, len, o)
|
||||||
if e < 0:
|
if e < 0:
|
||||||
e = s.len
|
e = s.len
|
||||||
result = s[o.. e-1]
|
result = s.substr(o, e-1)
|
||||||
|
|
||||||
const
|
const
|
||||||
alphaRanges = [
|
alphaRanges = [
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,49 @@ proc add*(url: var Url, a: Url) {.deprecated.} =
|
||||||
url = url / a
|
url = url / a
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
proc encodeUrl*(s: string): string =
|
||||||
|
## Encodes a value to be HTTP safe: This means that characters in the set
|
||||||
|
## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
|
||||||
|
## a space is converted to ``'+'`` and every other character is encoded as
|
||||||
|
## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
|
||||||
|
result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
|
||||||
|
for i in 0..s.len-1:
|
||||||
|
case s[i]
|
||||||
|
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
|
||||||
|
of ' ': add(result, '+')
|
||||||
|
else:
|
||||||
|
add(result, '%')
|
||||||
|
add(result, toHex(ord(s[i]), 2))
|
||||||
|
|
||||||
|
proc decodeUrl*(s: string): string =
|
||||||
|
## Decodes a value from its HTTP representation: This means that a ``'+'``
|
||||||
|
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
|
||||||
|
## value) is converted to the character with ordinal number ``xx``, and
|
||||||
|
## and every other character is carried over.
|
||||||
|
proc handleHexChar(c: char, x: var int) {.inline.} =
|
||||||
|
case c
|
||||||
|
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
|
||||||
|
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
|
||||||
|
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||||
|
else: assert(false)
|
||||||
|
|
||||||
|
result = newString(s.len)
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
while i < s.len:
|
||||||
|
case s[i]
|
||||||
|
of '%':
|
||||||
|
var x = 0
|
||||||
|
handleHexChar(s[i+1], x)
|
||||||
|
handleHexChar(s[i+2], x)
|
||||||
|
inc(i, 2)
|
||||||
|
result[j] = chr(x)
|
||||||
|
of '+': result[j] = ' '
|
||||||
|
else: result[j] = s[i]
|
||||||
|
inc(i)
|
||||||
|
inc(j)
|
||||||
|
setLen(result, j)
|
||||||
|
|
||||||
proc parseAuthority(authority: string, result: var Uri) =
|
proc parseAuthority(authority: string, result: var Uri) =
|
||||||
var i = 0
|
var i = 0
|
||||||
var inPort = false
|
var inPort = false
|
||||||
|
|
@ -327,6 +370,11 @@ proc `$`*(u: Uri): string =
|
||||||
result.add(u.anchor)
|
result.add(u.anchor)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
block:
|
||||||
|
const test1 = "abc\L+def xyz"
|
||||||
|
doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
|
||||||
|
doAssert decodeUrl(encodeUrl(test1)) == test1
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let str = "http://localhost"
|
let str = "http://localhost"
|
||||||
let test = parseUri(str)
|
let test = parseUri(str)
|
||||||
|
|
|
||||||
|
|
@ -1439,7 +1439,11 @@ const
|
||||||
## is the value that should be passed to `quit <#quit>`_ to indicate
|
## is the value that should be passed to `quit <#quit>`_ to indicate
|
||||||
## failure.
|
## failure.
|
||||||
|
|
||||||
var programResult* {.exportc: "nim_program_result".}: int
|
when defined(nodejs):
|
||||||
|
var programResult* {.importc: "process.exitCode".}: int
|
||||||
|
programResult = 0
|
||||||
|
else:
|
||||||
|
var programResult* {.exportc: "nim_program_result".}: int
|
||||||
## modify this variable to specify the exit code of the program
|
## modify this variable to specify the exit code of the program
|
||||||
## under normal circumstances. When the program is terminated
|
## under normal circumstances. When the program is terminated
|
||||||
## prematurely using ``quit``, this value is ignored.
|
## prematurely using ``quit``, this value is ignored.
|
||||||
|
|
@ -3525,7 +3529,10 @@ when hasAlloc or defined(nimscript):
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## var s = "abcdef"
|
## var s = "abcdef"
|
||||||
## assert s[1..3] == "bcd"
|
## assert s[1..3] == "bcd"
|
||||||
result = s.substr(s ^^ x.a, s ^^ x.b)
|
let a = s ^^ x.a
|
||||||
|
let L = (s ^^ x.b) - a + 1
|
||||||
|
result = newString(L)
|
||||||
|
for i in 0 ..< L: result[i] = s[i + a]
|
||||||
|
|
||||||
proc `[]=`*[T, U](s: var string, x: HSlice[T, U], b: string) =
|
proc `[]=`*[T, U](s: var string, x: HSlice[T, U], b: string) =
|
||||||
## slice assignment for strings. If
|
## slice assignment for strings. If
|
||||||
|
|
@ -3752,6 +3759,7 @@ template assert*(cond: bool, msg = "") =
|
||||||
## that ``AssertionError`` is hidden from the effect system, so it doesn't
|
## that ``AssertionError`` is hidden from the effect system, so it doesn't
|
||||||
## produce ``{.raises: [AssertionError].}``. This exception is only supposed
|
## produce ``{.raises: [AssertionError].}``. This exception is only supposed
|
||||||
## to be caught by unit testing frameworks.
|
## to be caught by unit testing frameworks.
|
||||||
|
##
|
||||||
## The compiler may not generate any code at all for ``assert`` if it is
|
## The compiler may not generate any code at all for ``assert`` if it is
|
||||||
## advised to do so through the ``-d:release`` or ``--assertions:off``
|
## advised to do so through the ``-d:release`` or ``--assertions:off``
|
||||||
## `command line switches <nimc.html#command-line-switches>`_.
|
## `command line switches <nimc.html#command-line-switches>`_.
|
||||||
|
|
@ -3992,3 +4000,38 @@ when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage):
|
||||||
proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32",
|
proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32",
|
||||||
importc: "SetConsoleOutputCP".}
|
importc: "SetConsoleOutputCP".}
|
||||||
discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
|
discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
|
||||||
|
|
||||||
|
|
||||||
|
when defined(nimHasRunnableExamples):
|
||||||
|
proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".}
|
||||||
|
## A section you should use to mark `runnable example`:idx: code with.
|
||||||
|
##
|
||||||
|
## - In normal debug and release builds code within
|
||||||
|
## a ``runnableExamples`` section is ignored.
|
||||||
|
## - The documentation generator is aware of these examples and considers them
|
||||||
|
## part of the ``##`` doc comment. As the last step of documentation
|
||||||
|
## generation the examples are put into an ``$file_example.nim`` file,
|
||||||
|
## compiled and tested. The collected examples are
|
||||||
|
## put into their own module to ensure the examples do not refer to
|
||||||
|
## non-exported symbols.
|
||||||
|
else:
|
||||||
|
template runnableExamples*(body: untyped) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
template doAssertRaises*(exception, code: untyped): typed =
|
||||||
|
## Raises ``AssertionError`` if specified ``code`` does not raise the
|
||||||
|
## specified exception.
|
||||||
|
runnableExamples:
|
||||||
|
doAssertRaises(ValueError):
|
||||||
|
raise newException(ValueError, "Hello World")
|
||||||
|
|
||||||
|
try:
|
||||||
|
block:
|
||||||
|
code
|
||||||
|
raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
|
||||||
|
except exception:
|
||||||
|
discard
|
||||||
|
except Exception as exc:
|
||||||
|
raiseAssert(astToStr(exception) &
|
||||||
|
" wasn't raised, another error was raised instead by:\n"&
|
||||||
|
astToStr(code))
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# Low level allocator for Nim. Has been designed to support the GC.
|
# Low level allocator for Nim. Has been designed to support the GC.
|
||||||
# TODO:
|
|
||||||
# - make searching for block O(1)
|
|
||||||
{.push profiler:off.}
|
{.push profiler:off.}
|
||||||
|
|
||||||
include osalloc
|
include osalloc
|
||||||
|
|
@ -19,14 +17,17 @@ template track(op, address, size) =
|
||||||
memTrackerOp(op, address, size)
|
memTrackerOp(op, address, size)
|
||||||
|
|
||||||
# We manage *chunks* of memory. Each chunk is a multiple of the page size.
|
# We manage *chunks* of memory. Each chunk is a multiple of the page size.
|
||||||
# Each chunk starts at an address that is divisible by the page size. Chunks
|
# Each chunk starts at an address that is divisible by the page size.
|
||||||
# that are bigger than ``ChunkOsReturn`` are returned back to the operating
|
|
||||||
# system immediately.
|
|
||||||
|
|
||||||
const
|
const
|
||||||
ChunkOsReturn = 256 * PageSize # 1 MB
|
InitialMemoryRequest = 128 * PageSize # 0.5 MB
|
||||||
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
|
|
||||||
SmallChunkSize = PageSize
|
SmallChunkSize = PageSize
|
||||||
|
MaxFli = 30
|
||||||
|
MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
|
||||||
|
# everywhere!
|
||||||
|
MaxSli = 1 shl MaxLog2Sli
|
||||||
|
FliOffset = 6
|
||||||
|
RealFli = MaxFli - FliOffset
|
||||||
|
|
||||||
type
|
type
|
||||||
PTrunk = ptr Trunk
|
PTrunk = ptr Trunk
|
||||||
|
|
@ -99,10 +100,12 @@ type
|
||||||
MemRegion = object
|
MemRegion = object
|
||||||
minLargeObj, maxLargeObj: int
|
minLargeObj, maxLargeObj: int
|
||||||
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
||||||
|
flBitmap: uint32
|
||||||
|
slBitmap: array[RealFli, uint32]
|
||||||
|
matrix: array[RealFli, array[MaxSli, PBigChunk]]
|
||||||
llmem: PLLChunk
|
llmem: PLLChunk
|
||||||
currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
|
currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
|
||||||
lastSize: int # needed for the case that OS gives us pages linearly
|
lastSize: int # needed for the case that OS gives us pages linearly
|
||||||
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
|
|
||||||
chunkStarts: IntSet
|
chunkStarts: IntSet
|
||||||
root, deleted, last, freeAvlNodes: PAvlNode
|
root, deleted, last, freeAvlNodes: PAvlNode
|
||||||
locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
|
locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
|
||||||
|
|
@ -110,7 +113,109 @@ type
|
||||||
bottomData: AvlNode
|
bottomData: AvlNode
|
||||||
heapLinks: HeapLinks
|
heapLinks: HeapLinks
|
||||||
|
|
||||||
{.deprecated: [TMemRegion: MemRegion].}
|
const
|
||||||
|
fsLookupTable: array[byte, int8] = [
|
||||||
|
-1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
|
5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||||
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||||
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7
|
||||||
|
]
|
||||||
|
|
||||||
|
proc msbit(x: uint32): int {.inline.} =
|
||||||
|
let a = if x <= 0xff_ff:
|
||||||
|
(if x <= 0xff: 0 else: 8)
|
||||||
|
else:
|
||||||
|
(if x <= 0xff_ff_ff: 16 else: 24)
|
||||||
|
result = int(fsLookupTable[byte(x shr a)]) + a
|
||||||
|
|
||||||
|
proc lsbit(x: uint32): int {.inline.} =
|
||||||
|
msbit(x and ((not x) + 1))
|
||||||
|
|
||||||
|
proc setBit(nr: int; dest: var uint32) {.inline.} =
|
||||||
|
dest = dest or (1u32 shl (nr and 0x1f))
|
||||||
|
|
||||||
|
proc clearBit(nr: int; dest: var uint32) {.inline.} =
|
||||||
|
dest = dest and not (1u32 shl (nr and 0x1f))
|
||||||
|
|
||||||
|
proc mappingSearch(r, fl, sl: var int) {.inline.} =
|
||||||
|
#let t = (1 shl (msbit(uint32 r) - MaxLog2Sli)) - 1
|
||||||
|
# This diverges from the standard TLSF algorithm because we need to ensure
|
||||||
|
# PageSize alignment:
|
||||||
|
let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
|
||||||
|
r = r + t
|
||||||
|
fl = msbit(uint32 r)
|
||||||
|
sl = (r shr (fl - MaxLog2Sli)) - MaxSli
|
||||||
|
dec fl, FliOffset
|
||||||
|
r = r and not t
|
||||||
|
sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned")
|
||||||
|
|
||||||
|
# See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of
|
||||||
|
# this algorithm.
|
||||||
|
|
||||||
|
proc mappingInsert(r: int): tuple[fl, sl: int] {.inline.} =
|
||||||
|
sysAssert((r and PageMask) == 0, "mappingInsert: still not aligned")
|
||||||
|
result.fl = msbit(uint32 r)
|
||||||
|
result.sl = (r shr (result.fl - MaxLog2Sli)) - MaxSli
|
||||||
|
dec result.fl, FliOffset
|
||||||
|
|
||||||
|
template mat(): untyped = a.matrix[fl][sl]
|
||||||
|
|
||||||
|
proc findSuitableBlock(a: MemRegion; fl, sl: var int): PBigChunk {.inline.} =
|
||||||
|
let tmp = a.slBitmap[fl] and (not 0u32 shl sl)
|
||||||
|
result = nil
|
||||||
|
if tmp != 0:
|
||||||
|
sl = lsbit(tmp)
|
||||||
|
result = mat()
|
||||||
|
else:
|
||||||
|
fl = lsbit(a.flBitmap and (not 0u32 shl (fl + 1)))
|
||||||
|
if fl > 0:
|
||||||
|
sl = lsbit(a.slBitmap[fl])
|
||||||
|
result = mat()
|
||||||
|
|
||||||
|
template clearBits(sl, fl) =
|
||||||
|
clearBit(sl, a.slBitmap[fl])
|
||||||
|
if a.slBitmap[fl] == 0u32:
|
||||||
|
# do not forget to cascade:
|
||||||
|
clearBit(fl, a.flBitmap)
|
||||||
|
|
||||||
|
proc removeChunkFromMatrix(a: var MemRegion; b: PBigChunk) =
|
||||||
|
let (fl, sl) = mappingInsert(b.size)
|
||||||
|
if b.next != nil: b.next.prev = b.prev
|
||||||
|
if b.prev != nil: b.prev.next = b.next
|
||||||
|
if mat() == b:
|
||||||
|
mat() = b.next
|
||||||
|
if mat() == nil:
|
||||||
|
clearBits(sl, fl)
|
||||||
|
b.prev = nil
|
||||||
|
b.next = nil
|
||||||
|
|
||||||
|
proc removeChunkFromMatrix2(a: var MemRegion; b: PBigChunk; fl, sl: int) =
|
||||||
|
mat() = b.next
|
||||||
|
if mat() != nil:
|
||||||
|
mat().prev = nil
|
||||||
|
else:
|
||||||
|
clearBits(sl, fl)
|
||||||
|
b.prev = nil
|
||||||
|
b.next = nil
|
||||||
|
|
||||||
|
proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
|
||||||
|
let (fl, sl) = mappingInsert(b.size)
|
||||||
|
b.prev = nil
|
||||||
|
b.next = mat()
|
||||||
|
if mat() != nil:
|
||||||
|
mat().prev = b
|
||||||
|
mat() = b
|
||||||
|
setBit(sl, a.slBitmap[fl])
|
||||||
|
setBit(fl, a.flBitmap)
|
||||||
|
|
||||||
{.push stack_trace: off.}
|
{.push stack_trace: off.}
|
||||||
proc initAllocator() = discard "nothing to do anymore"
|
proc initAllocator() = discard "nothing to do anymore"
|
||||||
|
|
@ -203,6 +308,7 @@ proc llDeallocAll(a: var MemRegion) =
|
||||||
var next = it.next
|
var next = it.next
|
||||||
osDeallocPages(it, PageSize)
|
osDeallocPages(it, PageSize)
|
||||||
it = next
|
it = next
|
||||||
|
a.llmem = nil
|
||||||
|
|
||||||
proc intSetGet(t: IntSet, key: int): PTrunk =
|
proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||||
var it = t.data[key and high(t.data)]
|
var it = t.data[key and high(t.data)]
|
||||||
|
|
@ -369,6 +475,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
|
||||||
result.prevSize = 0 or (result.prevSize and 1) # unknown
|
result.prevSize = 0 or (result.prevSize and 1) # unknown
|
||||||
# but do not overwrite 'used' field
|
# but do not overwrite 'used' field
|
||||||
a.lastSize = size # for next request
|
a.lastSize = size # for next request
|
||||||
|
sysAssert((cast[int](result) and PageMask) == 0, "requestOschunks: unaligned chunk")
|
||||||
|
|
||||||
proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
|
proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
|
||||||
result = contains(a.chunkStarts, pageIndex(p))
|
result = contains(a.chunkStarts, pageIndex(p))
|
||||||
|
|
@ -419,7 +526,7 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
|
||||||
if isAccessible(a, ri) and chunkUnused(ri):
|
if isAccessible(a, ri) and chunkUnused(ri):
|
||||||
sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
|
sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
|
||||||
if not isSmallChunk(ri):
|
if not isSmallChunk(ri):
|
||||||
listRemove(a.freeChunksList, cast[PBigChunk](ri))
|
removeChunkFromMatrix(a, cast[PBigChunk](ri))
|
||||||
inc(c.size, ri.size)
|
inc(c.size, ri.size)
|
||||||
excl(a.chunkStarts, pageIndex(ri))
|
excl(a.chunkStarts, pageIndex(ri))
|
||||||
when coalescLeft:
|
when coalescLeft:
|
||||||
|
|
@ -430,49 +537,44 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
|
||||||
if isAccessible(a, le) and chunkUnused(le):
|
if isAccessible(a, le) and chunkUnused(le):
|
||||||
sysAssert(not isSmallChunk(le), "freeBigChunk 5")
|
sysAssert(not isSmallChunk(le), "freeBigChunk 5")
|
||||||
if not isSmallChunk(le):
|
if not isSmallChunk(le):
|
||||||
listRemove(a.freeChunksList, cast[PBigChunk](le))
|
removeChunkFromMatrix(a, cast[PBigChunk](le))
|
||||||
inc(le.size, c.size)
|
inc(le.size, c.size)
|
||||||
excl(a.chunkStarts, pageIndex(c))
|
excl(a.chunkStarts, pageIndex(c))
|
||||||
c = cast[PBigChunk](le)
|
c = cast[PBigChunk](le)
|
||||||
|
|
||||||
incl(a, a.chunkStarts, pageIndex(c))
|
incl(a, a.chunkStarts, pageIndex(c))
|
||||||
updatePrevSize(a, c, c.size)
|
updatePrevSize(a, c, c.size)
|
||||||
listAdd(a.freeChunksList, c)
|
addChunkToMatrix(a, c)
|
||||||
# set 'used' to false:
|
# set 'used' to false:
|
||||||
c.prevSize = c.prevSize and not 1
|
c.prevSize = c.prevSize and not 1
|
||||||
|
|
||||||
proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
|
proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
|
||||||
var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
|
var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
|
||||||
sysAssert(rest notin a.freeChunksList, "splitChunk")
|
|
||||||
rest.size = c.size - size
|
rest.size = c.size - size
|
||||||
track("rest.origSize", addr rest.origSize, sizeof(int))
|
track("rest.origSize", addr rest.origSize, sizeof(int))
|
||||||
|
# XXX check if these two nil assignments are dead code given
|
||||||
|
# addChunkToMatrix's implementation:
|
||||||
rest.next = nil
|
rest.next = nil
|
||||||
rest.prev = nil
|
rest.prev = nil
|
||||||
# size and not used
|
# size and not used:
|
||||||
rest.prevSize = size
|
rest.prevSize = size
|
||||||
sysAssert((size and 1) == 0, "splitChunk 2")
|
sysAssert((size and 1) == 0, "splitChunk 2")
|
||||||
|
sysAssert((size and PageMask) == 0,
|
||||||
|
"splitChunk: size is not a multiple of the PageSize")
|
||||||
updatePrevSize(a, c, rest.size)
|
updatePrevSize(a, c, rest.size)
|
||||||
c.size = size
|
c.size = size
|
||||||
incl(a, a.chunkStarts, pageIndex(rest))
|
incl(a, a.chunkStarts, pageIndex(rest))
|
||||||
listAdd(a.freeChunksList, rest)
|
addChunkToMatrix(a, rest)
|
||||||
|
|
||||||
proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||||
# use first fit for now:
|
# use first fit for now:
|
||||||
sysAssert((size and PageMask) == 0, "getBigChunk 1")
|
|
||||||
sysAssert(size > 0, "getBigChunk 2")
|
sysAssert(size > 0, "getBigChunk 2")
|
||||||
result = a.freeChunksList
|
var size = size # roundup(size, PageSize)
|
||||||
block search:
|
var fl, sl: int
|
||||||
while result != nil:
|
mappingSearch(size, fl, sl)
|
||||||
sysAssert chunkUnused(result), "getBigChunk 3"
|
sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
|
||||||
if result.size == size:
|
result = findSuitableBlock(a, fl, sl)
|
||||||
listRemove(a.freeChunksList, result)
|
if result == nil:
|
||||||
break search
|
|
||||||
elif result.size > size:
|
|
||||||
listRemove(a.freeChunksList, result)
|
|
||||||
splitChunk(a, result, size)
|
|
||||||
break search
|
|
||||||
result = result.next
|
|
||||||
sysAssert result != a.freeChunksList, "getBigChunk 4"
|
|
||||||
if size < InitialMemoryRequest:
|
if size < InitialMemoryRequest:
|
||||||
result = requestOsChunks(a, InitialMemoryRequest)
|
result = requestOsChunks(a, InitialMemoryRequest)
|
||||||
splitChunk(a, result, size)
|
splitChunk(a, result, size)
|
||||||
|
|
@ -481,7 +583,10 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||||
# if we over allocated split the chunk:
|
# if we over allocated split the chunk:
|
||||||
if result.size > size:
|
if result.size > size:
|
||||||
splitChunk(a, result, size)
|
splitChunk(a, result, size)
|
||||||
|
else:
|
||||||
|
removeChunkFromMatrix2(a, result, fl, sl)
|
||||||
|
if result.size >= size + PageSize:
|
||||||
|
splitChunk(a, result, size)
|
||||||
# set 'used' to to true:
|
# set 'used' to to true:
|
||||||
result.prevSize = 1
|
result.prevSize = 1
|
||||||
track("setUsedToFalse", addr result.origSize, sizeof(int))
|
track("setUsedToFalse", addr result.origSize, sizeof(int))
|
||||||
|
|
@ -572,14 +677,14 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||||
size == 0, "rawAlloc 21")
|
size == 0, "rawAlloc 21")
|
||||||
sysAssert(allocInv(a), "rawAlloc: end small size")
|
sysAssert(allocInv(a), "rawAlloc: end small size")
|
||||||
else:
|
else:
|
||||||
size = roundup(requestedSize+bigChunkOverhead(), PageSize)
|
size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize)
|
||||||
# allocate a large block
|
# allocate a large block
|
||||||
var c = getBigChunk(a, size)
|
var c = getBigChunk(a, size)
|
||||||
sysAssert c.prev == nil, "rawAlloc 10"
|
sysAssert c.prev == nil, "rawAlloc 10"
|
||||||
sysAssert c.next == nil, "rawAlloc 11"
|
sysAssert c.next == nil, "rawAlloc 11"
|
||||||
sysAssert c.size == size, "rawAlloc 12"
|
|
||||||
result = addr(c.data)
|
result = addr(c.data)
|
||||||
sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13")
|
sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
|
||||||
|
sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
|
||||||
if a.root == nil: a.root = getBottom(a)
|
if a.root == nil: a.root = getBottom(a)
|
||||||
add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
|
add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
|
||||||
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ type
|
||||||
# Page size of the system; in most cases 4096 bytes. For exotic OS or
|
# Page size of the system; in most cases 4096 bytes. For exotic OS or
|
||||||
# CPU this needs to be changed:
|
# CPU this needs to be changed:
|
||||||
const
|
const
|
||||||
PageShift = when defined(cpu16): 8 else: 12
|
PageShift = when defined(cpu16): 8 else: 12 # \
|
||||||
|
# my tests showed no improvments for using larger page sizes.
|
||||||
PageSize = 1 shl PageShift
|
PageSize = 1 shl PageShift
|
||||||
PageMask = PageSize-1
|
PageMask = PageSize-1
|
||||||
|
|
||||||
|
|
@ -343,7 +344,6 @@ elif defined(gogc):
|
||||||
|
|
||||||
const goFlagNoZero: uint32 = 1 shl 3
|
const goFlagNoZero: uint32 = 1 shl 3
|
||||||
proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
|
proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
|
||||||
proc goFree(v: pointer) {.importc: "__go_free", dynlib: goLib.}
|
|
||||||
|
|
||||||
proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
|
proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
|
||||||
|
|
||||||
|
|
@ -376,7 +376,6 @@ elif defined(gogc):
|
||||||
result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
|
result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
|
||||||
copyMem(result, old, oldsize)
|
copyMem(result, old, oldsize)
|
||||||
zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
|
zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
|
||||||
goFree(old)
|
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
|
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
|
||||||
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
|
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
|
||||||
|
|
@ -573,3 +572,11 @@ when not declared(nimNewSeqOfCap):
|
||||||
cast[PGenericSeq](result).reserved = cap
|
cast[PGenericSeq](result).reserved = cap
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
when not declared(ForeignCell):
|
||||||
|
type ForeignCell* = object
|
||||||
|
data*: pointer
|
||||||
|
|
||||||
|
proc protect*(x: pointer): ForeignCell = ForeignCell(data: x)
|
||||||
|
proc dispose*(x: ForeignCell) = discard
|
||||||
|
proc isNotForeign*(x: ForeignCell): bool = false
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ elif defined(windows):
|
||||||
# space heavily, so we now treat Windows as a strange unmap target.
|
# space heavily, so we now treat Windows as a strange unmap target.
|
||||||
when reallyOsDealloc:
|
when reallyOsDealloc:
|
||||||
if virtualFree(p, 0, MEM_RELEASE) == 0:
|
if virtualFree(p, 0, MEM_RELEASE) == 0:
|
||||||
cprintf "yes, failing!"
|
cprintf "virtualFree failing!"
|
||||||
quit 1
|
quit 1
|
||||||
#VirtualFree(p, size, MEM_DECOMMIT)
|
#VirtualFree(p, size, MEM_DECOMMIT)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
||||||
result.reserved = r
|
result.reserved = r
|
||||||
|
|
||||||
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||||
compilerRtl.} =
|
compilerRtl, inl.} =
|
||||||
result = seq
|
result = seq
|
||||||
if result.space < newLen:
|
if result.space < newLen:
|
||||||
let r = max(resize(result.space), newLen)
|
let r = max(resize(result.space), newLen)
|
||||||
|
|
@ -282,10 +282,11 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||||
doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
|
doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
|
||||||
gch.tempStack.len = len0
|
gch.tempStack.len = len0
|
||||||
else:
|
else:
|
||||||
for i in newLen..result.len-1:
|
if ntfNoRefs notin extGetCellType(result).base.flags:
|
||||||
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
|
for i in newLen..result.len-1:
|
||||||
GenericSeqSize +% (i*%elemSize)),
|
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
|
||||||
extGetCellType(result).base, waZctDecRef)
|
GenericSeqSize +% (i*%elemSize)),
|
||||||
|
extGetCellType(result).base, waZctDecRef)
|
||||||
|
|
||||||
# XXX: zeroing out the memory can still result in crashes if a wiped-out
|
# XXX: zeroing out the memory can still result in crashes if a wiped-out
|
||||||
# cell is aliased by another pointer (ie proc parameter or a let variable).
|
# cell is aliased by another pointer (ie proc parameter or a let variable).
|
||||||
|
|
|
||||||
|
|
@ -255,9 +255,9 @@ when emulatedThreadVars:
|
||||||
proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
|
proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
|
||||||
|
|
||||||
# we preallocate a fixed size for thread local storage, so that no heap
|
# we preallocate a fixed size for thread local storage, so that no heap
|
||||||
# allocations are needed. Currently less than 7K are used on a 64bit machine.
|
# allocations are needed. Currently less than 16K are used on a 64bit machine.
|
||||||
# We use ``float`` for proper alignment:
|
# We use ``float`` for proper alignment:
|
||||||
const nimTlsSize {.intdefine.} = 8000
|
const nimTlsSize {.intdefine.} = 16000
|
||||||
type
|
type
|
||||||
ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
|
ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,7 @@ var
|
||||||
SO_DONTLINGER* {.importc, header: "winsock2.h".}: cint
|
SO_DONTLINGER* {.importc, header: "winsock2.h".}: cint
|
||||||
SO_EXCLUSIVEADDRUSE* {.importc, header: "winsock2.h".}: cint # disallow local address reuse
|
SO_EXCLUSIVEADDRUSE* {.importc, header: "winsock2.h".}: cint # disallow local address reuse
|
||||||
SO_ERROR* {.importc, header: "winsock2.h".}: cint
|
SO_ERROR* {.importc, header: "winsock2.h".}: cint
|
||||||
|
TCP_NODELAY* {.importc, header: "winsock2.h".}: cint
|
||||||
|
|
||||||
proc `==`*(x, y: SocketHandle): bool {.borrow.}
|
proc `==`*(x, y: SocketHandle): bool {.borrow.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,9 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||||
of cmdEnd: break
|
of cmdEnd: break
|
||||||
of cmdLongoption, cmdShortOption:
|
of cmdLongoption, cmdShortOption:
|
||||||
case p.key.normalize
|
case p.key.normalize
|
||||||
|
of "help":
|
||||||
|
stdout.writeline(Usage)
|
||||||
|
quit()
|
||||||
of "port":
|
of "port":
|
||||||
gPort = parseInt(p.val).Port
|
gPort = parseInt(p.val).Port
|
||||||
gMode = mtcp
|
gMode = mtcp
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,16 @@ proc testVarargs(x, y, z: int): seq[int] =
|
||||||
|
|
||||||
result = waitFor all(a, b, c)
|
result = waitFor all(a, b, c)
|
||||||
|
|
||||||
|
proc testWithDupes() =
|
||||||
|
var
|
||||||
|
tasks = newSeq[Future[void]](taskCount)
|
||||||
|
fut = futureWithoutValue()
|
||||||
|
|
||||||
|
for i in 0..<taskCount:
|
||||||
|
tasks[i] = fut
|
||||||
|
|
||||||
|
waitFor all(tasks)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
startTime = cpuTime()
|
startTime = cpuTime()
|
||||||
|
|
@ -57,6 +67,13 @@ block:
|
||||||
|
|
||||||
doAssert execTime * 1000 < taskCount * sleepDuration
|
doAssert execTime * 1000 < taskCount * sleepDuration
|
||||||
|
|
||||||
|
block:
|
||||||
|
let startTime = cpuTime()
|
||||||
|
testWithDupes()
|
||||||
|
let execTime = cpuTime() - startTime
|
||||||
|
|
||||||
|
doAssert execTime * 1000 < taskCount * sleepDuration
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
startTime = cpuTime()
|
startTime = cpuTime()
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,19 @@ proc main() {.async.} =
|
||||||
doAssert data == "foot\ntest2"
|
doAssert data == "foot\ntest2"
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
|
# Issue #5531
|
||||||
|
block:
|
||||||
|
removeFile(fn)
|
||||||
|
var file = openAsync(fn, fmWrite)
|
||||||
|
await file.write("test2")
|
||||||
|
file.close()
|
||||||
|
file = openAsync(fn, fmWrite)
|
||||||
|
await file.write("test3")
|
||||||
|
file.close()
|
||||||
|
file = openAsync(fn, fmRead)
|
||||||
|
let data = await file.readAll()
|
||||||
|
doAssert data == "test3"
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
|
||||||
waitFor main()
|
waitFor main()
|
||||||
|
|
|
||||||
18
tests/ccgbugs/t6756.nim
Normal file
18
tests/ccgbugs/t6756.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import typetraits
|
||||||
|
type
|
||||||
|
A[T] = ref object
|
||||||
|
v: T
|
||||||
|
|
||||||
|
template templ(o: A, op: untyped): untyped =
|
||||||
|
type T = type(o.v)
|
||||||
|
|
||||||
|
var res: A[T]
|
||||||
|
|
||||||
|
block:
|
||||||
|
var it {.inject.}: T
|
||||||
|
it = o.v
|
||||||
|
res = A[T](v: op)
|
||||||
|
res
|
||||||
|
|
||||||
|
let a = A[int](v: 1)
|
||||||
|
echo templ(a, it + 2)[]
|
||||||
|
|
@ -85,3 +85,31 @@ proc go() =
|
||||||
echo "vidx ", $vidx(hg, 1, 2, hiC)
|
echo "vidx ", $vidx(hg, 1, 2, hiC)
|
||||||
|
|
||||||
go()
|
go()
|
||||||
|
|
||||||
|
# another sighashes problem: In tuples we have to ignore ranges.
|
||||||
|
|
||||||
|
type
|
||||||
|
Position = tuple[x, y: int16]
|
||||||
|
n16 = range[0'i16..high(int16)]
|
||||||
|
|
||||||
|
proc print(pos: Position) =
|
||||||
|
echo $pos.x, ",", $pos.y
|
||||||
|
|
||||||
|
var x = 0.n16
|
||||||
|
var y = 0.n16
|
||||||
|
print((x, y))
|
||||||
|
|
||||||
|
|
||||||
|
# bug #6889
|
||||||
|
proc createProgressSetterWithPropSetter[T](setter: proc(v: T)) = discard
|
||||||
|
|
||||||
|
type A = distinct array[4, float32]
|
||||||
|
type B = distinct array[3, float32]
|
||||||
|
|
||||||
|
type Foo[T] = tuple
|
||||||
|
setter: proc(v: T)
|
||||||
|
|
||||||
|
proc getFoo[T](): Foo[T] = discard
|
||||||
|
|
||||||
|
createProgressSetterWithPropSetter(getFoo[A]().setter)
|
||||||
|
createProgressSetterWithPropSetter(getFoo[B]().setter)
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,66 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim c --verbosity:0 --colors:off $file"
|
cmd: "nim c --verbosity:0 --colors:off $file"
|
||||||
nimout: '''
|
nimout: '''
|
||||||
texplain.nim(99, 10) Hint: Non-matching candidates for e(y)
|
texplain.nim(103, 10) Hint: Non-matching candidates for e(y)
|
||||||
proc e(i: int): int
|
proc e(i: int): int
|
||||||
|
|
||||||
texplain.nim(102, 7) Hint: Non-matching candidates for e(10)
|
texplain.nim(106, 7) Hint: Non-matching candidates for e(10)
|
||||||
proc e(o: ExplainedConcept): int
|
proc e(o: ExplainedConcept): int
|
||||||
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
|
texplain.nim(69, 6) ExplainedConcept: undeclared field: 'foo'
|
||||||
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
|
texplain.nim(69, 6) ExplainedConcept: undeclared field: '.'
|
||||||
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
|
texplain.nim(69, 6) ExplainedConcept: expression '.' cannot be called
|
||||||
texplain.nim(65, 5) ExplainedConcept: concept predicate failed
|
texplain.nim(69, 5) ExplainedConcept: concept predicate failed
|
||||||
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
|
texplain.nim(70, 6) ExplainedConcept: undeclared field: 'bar'
|
||||||
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
|
texplain.nim(70, 6) ExplainedConcept: undeclared field: '.'
|
||||||
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
|
texplain.nim(70, 6) ExplainedConcept: expression '.' cannot be called
|
||||||
texplain.nim(65, 5) ExplainedConcept: concept predicate failed
|
texplain.nim(69, 5) ExplainedConcept: concept predicate failed
|
||||||
|
|
||||||
texplain.nim(105, 10) Hint: Non-matching candidates for e(10)
|
texplain.nim(109, 10) Hint: Non-matching candidates for e(10)
|
||||||
proc e(o: ExplainedConcept): int
|
proc e(o: ExplainedConcept): int
|
||||||
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
|
texplain.nim(69, 6) ExplainedConcept: undeclared field: 'foo'
|
||||||
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
|
texplain.nim(69, 6) ExplainedConcept: undeclared field: '.'
|
||||||
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
|
texplain.nim(69, 6) ExplainedConcept: expression '.' cannot be called
|
||||||
texplain.nim(65, 5) ExplainedConcept: concept predicate failed
|
texplain.nim(69, 5) ExplainedConcept: concept predicate failed
|
||||||
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
|
texplain.nim(70, 6) ExplainedConcept: undeclared field: 'bar'
|
||||||
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
|
texplain.nim(70, 6) ExplainedConcept: undeclared field: '.'
|
||||||
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
|
texplain.nim(70, 6) ExplainedConcept: expression '.' cannot be called
|
||||||
texplain.nim(65, 5) ExplainedConcept: concept predicate failed
|
texplain.nim(69, 5) ExplainedConcept: concept predicate failed
|
||||||
|
|
||||||
texplain.nim(109, 20) Error: type mismatch: got (NonMatchingType)
|
texplain.nim(113, 20) Error: type mismatch: got (NonMatchingType)
|
||||||
but expected one of:
|
but expected one of:
|
||||||
proc e(o: ExplainedConcept): int
|
proc e(o: ExplainedConcept): int
|
||||||
texplain.nim(65, 5) ExplainedConcept: concept predicate failed
|
texplain.nim(69, 5) ExplainedConcept: concept predicate failed
|
||||||
proc e(i: int): int
|
proc e(i: int): int
|
||||||
|
|
||||||
texplain.nim(110, 20) Error: type mismatch: got (NonMatchingType)
|
expression: e(n)
|
||||||
|
texplain.nim(114, 20) Error: type mismatch: got (NonMatchingType)
|
||||||
but expected one of:
|
but expected one of:
|
||||||
proc r(o: RegularConcept): int
|
proc r(o: RegularConcept): int
|
||||||
texplain.nim(69, 5) RegularConcept: concept predicate failed
|
texplain.nim(73, 5) RegularConcept: concept predicate failed
|
||||||
proc r[T](a: SomeNumber; b: T; c: auto)
|
proc r[T](a: SomeNumber; b: T; c: auto)
|
||||||
proc r(i: string): int
|
proc r(i: string): int
|
||||||
|
|
||||||
texplain.nim(111, 20) Hint: Non-matching candidates for r(y)
|
expression: r(n)
|
||||||
|
texplain.nim(115, 20) Hint: Non-matching candidates for r(y)
|
||||||
proc r[T](a: SomeNumber; b: T; c: auto)
|
proc r[T](a: SomeNumber; b: T; c: auto)
|
||||||
proc r(i: string): int
|
proc r(i: string): int
|
||||||
|
|
||||||
texplain.nim(119, 2) Error: type mismatch: got (MatchingType)
|
texplain.nim(123, 2) Error: type mismatch: got (MatchingType)
|
||||||
but expected one of:
|
but expected one of:
|
||||||
proc f(o: NestedConcept)
|
proc f(o: NestedConcept)
|
||||||
texplain.nim(69, 6) RegularConcept: undeclared field: 'foo'
|
texplain.nim(73, 6) RegularConcept: undeclared field: 'foo'
|
||||||
texplain.nim(69, 6) RegularConcept: undeclared field: '.'
|
texplain.nim(73, 6) RegularConcept: undeclared field: '.'
|
||||||
texplain.nim(69, 6) RegularConcept: expression '.' cannot be called
|
texplain.nim(73, 6) RegularConcept: expression '.' cannot be called
|
||||||
texplain.nim(69, 5) RegularConcept: concept predicate failed
|
texplain.nim(73, 5) RegularConcept: concept predicate failed
|
||||||
texplain.nim(70, 6) RegularConcept: undeclared field: 'bar'
|
texplain.nim(74, 6) RegularConcept: undeclared field: 'bar'
|
||||||
texplain.nim(70, 6) RegularConcept: undeclared field: '.'
|
texplain.nim(74, 6) RegularConcept: undeclared field: '.'
|
||||||
texplain.nim(70, 6) RegularConcept: expression '.' cannot be called
|
texplain.nim(74, 6) RegularConcept: expression '.' cannot be called
|
||||||
texplain.nim(69, 5) RegularConcept: concept predicate failed
|
texplain.nim(73, 5) RegularConcept: concept predicate failed
|
||||||
texplain.nim(73, 5) NestedConcept: concept predicate failed
|
texplain.nim(77, 5) NestedConcept: concept predicate failed
|
||||||
|
|
||||||
|
expression: f(y)
|
||||||
'''
|
'''
|
||||||
line: 119
|
line: 123
|
||||||
errormsg: "type mismatch: got (MatchingType)"
|
errormsg: "type mismatch: got (MatchingType)"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
cmd: "nim cpp $file"
|
||||||
output: ""
|
output: ""
|
||||||
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
block: #5979
|
block: #5979
|
||||||
|
|
|
||||||
101
tests/destructor/tatomicptrs.nim
Normal file
101
tests/destructor/tatomicptrs.nim
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
discard """
|
||||||
|
output: '''allocating
|
||||||
|
allocating
|
||||||
|
allocating
|
||||||
|
55
|
||||||
|
60
|
||||||
|
99
|
||||||
|
deallocating
|
||||||
|
deallocating
|
||||||
|
deallocating
|
||||||
|
'''
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
SharedPtr*[T] = object
|
||||||
|
x: ptr T
|
||||||
|
|
||||||
|
#proc isNil[T](s: SharedPtr[T]): bool {.inline.} = s.x.isNil
|
||||||
|
|
||||||
|
template incRef(x) =
|
||||||
|
atomicInc(x.refcount)
|
||||||
|
|
||||||
|
template decRef(x): untyped = atomicDec(x.refcount)
|
||||||
|
|
||||||
|
proc makeShared*[T](x: T): SharedPtr[T] =
|
||||||
|
# XXX could benefit from 'sink' parameter.
|
||||||
|
# XXX could benefit from a macro that generates it.
|
||||||
|
result = cast[SharedPtr[T]](allocShared(sizeof(x)))
|
||||||
|
result.x[] = x
|
||||||
|
echo "allocating"
|
||||||
|
|
||||||
|
proc `=destroy`*[T](dest: var SharedPtr[T]) =
|
||||||
|
var s = dest.x
|
||||||
|
if s != nil and decRef(s) == 0:
|
||||||
|
`=destroy`(s[])
|
||||||
|
deallocShared(s)
|
||||||
|
echo "deallocating"
|
||||||
|
dest.x = nil
|
||||||
|
|
||||||
|
proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
|
||||||
|
var s = src.x
|
||||||
|
if s != nil: incRef(s)
|
||||||
|
#atomicSwap(dest, s)
|
||||||
|
# XXX use an atomic store here:
|
||||||
|
swap(dest.x, s)
|
||||||
|
if s != nil and decRef(s) == 0:
|
||||||
|
`=destroy`(s[])
|
||||||
|
deallocShared(s)
|
||||||
|
echo "deallocating"
|
||||||
|
|
||||||
|
proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
|
||||||
|
## XXX make this an atomic store:
|
||||||
|
if dest.x != src.x:
|
||||||
|
let s = dest.x
|
||||||
|
if s != nil:
|
||||||
|
`=destroy`(s[])
|
||||||
|
deallocShared(s)
|
||||||
|
echo "deallocating"
|
||||||
|
dest.x = src.x
|
||||||
|
|
||||||
|
template `.`*[T](s: SharedPtr[T]; field: untyped): untyped =
|
||||||
|
s.x.field
|
||||||
|
|
||||||
|
template `.=`*[T](s: SharedPtr[T]; field, value: untyped) =
|
||||||
|
s.x.field = value
|
||||||
|
|
||||||
|
from macros import unpackVarargs
|
||||||
|
|
||||||
|
template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped =
|
||||||
|
unpackVarargs(s.x.field, args)
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Tree = SharedPtr[TreeObj]
|
||||||
|
TreeObj = object
|
||||||
|
refcount: int
|
||||||
|
le, ri: Tree
|
||||||
|
data: int
|
||||||
|
|
||||||
|
proc takesTree(a: Tree) =
|
||||||
|
if not a.isNil:
|
||||||
|
takesTree(a.le)
|
||||||
|
echo a.data
|
||||||
|
takesTree(a.ri)
|
||||||
|
|
||||||
|
proc createTree(data: int): Tree =
|
||||||
|
result = makeShared(TreeObj(refcount: 1, data: data))
|
||||||
|
|
||||||
|
proc createTree(data: int; le, ri: Tree): Tree =
|
||||||
|
result = makeShared(TreeObj(refcount: 1, le: le, ri: ri, data: data))
|
||||||
|
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let le = createTree(55)
|
||||||
|
let ri = createTree(99)
|
||||||
|
let t = createTree(60, le, ri)
|
||||||
|
takesTree(t)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
@ -20,10 +20,10 @@ myobj destroyed
|
||||||
----
|
----
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
'''
|
'''
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
disabled: "true"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
{.experimental.}
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TMyObj = object
|
TMyObj = object
|
||||||
x, y: int
|
x, y: int
|
||||||
|
|
@ -61,7 +61,7 @@ proc `=destroy`(o: var TMyObj) =
|
||||||
if o.p != nil: dealloc o.p
|
if o.p != nil: dealloc o.p
|
||||||
echo "myobj destroyed"
|
echo "myobj destroyed"
|
||||||
|
|
||||||
proc `=destroy`(o: var TMyGeneric1) =
|
proc `=destroy`(o: var TMyGeneric1[int]) =
|
||||||
echo "mygeneric1 destroyed"
|
echo "mygeneric1 destroyed"
|
||||||
|
|
||||||
proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
|
proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
discard """
|
|
||||||
line: 23
|
|
||||||
nimout: " usage of a type with a destructor in a non destructible context"
|
|
||||||
"""
|
|
||||||
|
|
||||||
{.experimental.}
|
|
||||||
|
|
||||||
type
|
|
||||||
TMyObj = object
|
|
||||||
x, y: int
|
|
||||||
p: pointer
|
|
||||||
|
|
||||||
proc `=destroy`(o: var TMyObj) =
|
|
||||||
if o.p != nil: dealloc o.p
|
|
||||||
|
|
||||||
proc open: TMyObj =
|
|
||||||
result = TMyObj(x: 1, y: 2, p: alloc(3))
|
|
||||||
|
|
||||||
|
|
||||||
proc `$`(x: TMyObj): string = $x.y
|
|
||||||
|
|
||||||
proc foo =
|
|
||||||
discard open()
|
|
||||||
|
|
||||||
# XXX doesn't trigger this yet:
|
|
||||||
#echo open()
|
|
||||||
|
|
||||||
|
|
@ -2,14 +2,14 @@ discard """
|
||||||
output: '''assign
|
output: '''assign
|
||||||
destroy
|
destroy
|
||||||
destroy
|
destroy
|
||||||
destroy Foo: 5
|
|
||||||
5
|
5
|
||||||
destroy Foo: 123
|
123
|
||||||
123'''
|
destroy Foo: 5
|
||||||
|
destroy Foo: 123'''
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #2821
|
# bug #2821
|
||||||
{.experimental.}
|
|
||||||
|
|
||||||
type T = object
|
type T = object
|
||||||
|
|
||||||
|
|
|
||||||
59
tests/destructor/tmove_objconstr.nim
Normal file
59
tests/destructor/tmove_objconstr.nim
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: '''test created
|
||||||
|
test destroyed 0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
Pony is dying!'''
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #4214
|
||||||
|
type
|
||||||
|
Data = object
|
||||||
|
data: string
|
||||||
|
rc: int
|
||||||
|
|
||||||
|
proc `=destroy`(d: var Data) =
|
||||||
|
dec d.rc
|
||||||
|
echo d.data, " destroyed ", d.rc
|
||||||
|
|
||||||
|
proc `=`(dst: var Data, src: Data) =
|
||||||
|
echo src.data, " copied"
|
||||||
|
dst.data = src.data & " (copy)"
|
||||||
|
dec dst.rc
|
||||||
|
inc dst.rc
|
||||||
|
|
||||||
|
proc initData(s: string): Data =
|
||||||
|
result = Data(data: s, rc: 1)
|
||||||
|
echo s, " created"
|
||||||
|
|
||||||
|
proc pointlessWrapper(s: string): Data =
|
||||||
|
result = initData(s)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var x = pointlessWrapper"test"
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
|
|
||||||
|
# bug #985
|
||||||
|
|
||||||
|
type
|
||||||
|
Pony = object
|
||||||
|
name: string
|
||||||
|
|
||||||
|
proc `=destroy`(o: var Pony) =
|
||||||
|
echo "Pony is dying!"
|
||||||
|
|
||||||
|
proc getPony: Pony =
|
||||||
|
result.name = "Sparkles"
|
||||||
|
|
||||||
|
iterator items(p: Pony): int =
|
||||||
|
for i in 1..4:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
for x in getPony():
|
||||||
|
echo x
|
||||||
7629
tests/fragmentation/data.nim
Normal file
7629
tests/fragmentation/data.nim
Normal file
File diff suppressed because it is too large
Load diff
27
tests/fragmentation/tfragment_alloc.nim
Normal file
27
tests/fragmentation/tfragment_alloc.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: '''occupied ok: true
|
||||||
|
total ok: true'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import strutils, data
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var m = 0
|
||||||
|
for i in 0..1000_000:
|
||||||
|
let size = sizes[i mod sizes.len]
|
||||||
|
let p = alloc(size)
|
||||||
|
if p == nil:
|
||||||
|
quit "could not serve request!"
|
||||||
|
dealloc p
|
||||||
|
# c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
let occ = getOccupiedMem()
|
||||||
|
let total = getTotalMem()
|
||||||
|
|
||||||
|
# Current values on Win64: 824KiB / 106.191MiB
|
||||||
|
|
||||||
|
echo "occupied ok: ", occ < 2 * 1024 * 1024
|
||||||
|
echo "total ok: ", total < 120 * 1024 * 1024
|
||||||
27
tests/fragmentation/tfragment_gc.nim
Normal file
27
tests/fragmentation/tfragment_gc.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
discard """
|
||||||
|
output: '''occupied ok: true
|
||||||
|
total ok: true'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import strutils, data
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var m = 0
|
||||||
|
# Since the GC test is slower than the alloc test, we only iterate 100_000 times here:
|
||||||
|
for i in 0..100_000:
|
||||||
|
let size = sizes[i mod sizes.len]
|
||||||
|
let p = newString(size)
|
||||||
|
# c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
let occ = getOccupiedMem()
|
||||||
|
let total = getTotalMem()
|
||||||
|
|
||||||
|
# Concrete values on Win64: 58.152MiB / 188.285MiB
|
||||||
|
|
||||||
|
echo "occupied ok: ", occ < 60 * 1024 * 1024
|
||||||
|
let totalOk = total < 210 * 1024 * 1024
|
||||||
|
if not totalOk:
|
||||||
|
echo "total peak memory ", formatSize(total)
|
||||||
|
echo "total ok: ", totalOk
|
||||||
11
tests/js/tcodegendeclproc.nim
Normal file
11
tests/js/tcodegendeclproc.nim
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
-1
|
||||||
|
8
|
||||||
|
'''
|
||||||
|
ccodecheck: "'console.log(-1); function fac_' \\d+ '(n_' \\d+ ')'"
|
||||||
|
"""
|
||||||
|
proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} =
|
||||||
|
return n
|
||||||
|
|
||||||
|
echo fac(8)
|
||||||
10
tests/js/tcodegendeclvar.nim
Normal file
10
tests/js/tcodegendeclvar.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
-1
|
||||||
|
2
|
||||||
|
'''
|
||||||
|
ccodecheck: "'console.log(-1); var v_' \\d+ ' = [2]'"
|
||||||
|
"""
|
||||||
|
|
||||||
|
var v {.codegenDecl: "console.log(-1); var $2".} = 2
|
||||||
|
echo v
|
||||||
|
|
@ -27,9 +27,10 @@ macro testX(x,inst0: typed; recurse: static[bool]; implX: typed): typed =
|
||||||
let inst = x.getTypeInst
|
let inst = x.getTypeInst
|
||||||
let instr = inst.symToIdent.treeRepr
|
let instr = inst.symToIdent.treeRepr
|
||||||
let inst0r = inst0.symToIdent.treeRepr
|
let inst0r = inst0.symToIdent.treeRepr
|
||||||
#echo instr
|
if instr != inst0r:
|
||||||
#echo inst0r
|
echo "instr:\n", instr
|
||||||
doAssert(instr == inst0r)
|
echo "inst0r:\n", inst0r
|
||||||
|
doAssert(instr == inst0r)
|
||||||
|
|
||||||
# check that getTypeImpl(x) is correct
|
# check that getTypeImpl(x) is correct
|
||||||
# if implX is nil then compare to inst0
|
# if implX is nil then compare to inst0
|
||||||
|
|
@ -41,9 +42,10 @@ macro testX(x,inst0: typed; recurse: static[bool]; implX: typed): typed =
|
||||||
else: implX[0][2]
|
else: implX[0][2]
|
||||||
let implr = impl.symToIdent.treerepr
|
let implr = impl.symToIdent.treerepr
|
||||||
let impl0r = impl0.symToIdent.treerepr
|
let impl0r = impl0.symToIdent.treerepr
|
||||||
#echo implr
|
if implr != impl0r:
|
||||||
#echo impl0r
|
echo "implr:\n", implr
|
||||||
doAssert(implr == impl0r)
|
echo "impl0r:\n", impl0r
|
||||||
|
doAssert(implr == impl0r)
|
||||||
|
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
#template echoString(s: string) = echo s.replace("\n","\n ")
|
#template echoString(s: string) = echo s.replace("\n","\n ")
|
||||||
|
|
@ -111,6 +113,18 @@ type
|
||||||
Generic[T] = seq[int]
|
Generic[T] = seq[int]
|
||||||
Concrete = Generic[int]
|
Concrete = Generic[int]
|
||||||
|
|
||||||
|
Generic2[T1, T2] = seq[T1]
|
||||||
|
Concrete2 = Generic2[int, float]
|
||||||
|
|
||||||
|
Alias1 = float
|
||||||
|
Alias2 = Concrete
|
||||||
|
Alias3 = Concrete2
|
||||||
|
|
||||||
|
Vec[N: static[int],T] = object
|
||||||
|
arr: array[N,T]
|
||||||
|
Vec4[T] = Vec[4,T]
|
||||||
|
|
||||||
|
|
||||||
test(bool)
|
test(bool)
|
||||||
test(char)
|
test(char)
|
||||||
test(int)
|
test(int)
|
||||||
|
|
@ -144,11 +158,27 @@ test(Tree):
|
||||||
left: ref Tree
|
left: ref Tree
|
||||||
right: ref Tree
|
right: ref Tree
|
||||||
test(Concrete):
|
test(Concrete):
|
||||||
type _ = Generic[int]
|
type _ = seq[int]
|
||||||
test(Generic[int]):
|
test(Generic[int]):
|
||||||
type _ = seq[int]
|
type _ = seq[int]
|
||||||
test(Generic[float]):
|
test(Generic[float]):
|
||||||
type _ = seq[int]
|
type _ = seq[int]
|
||||||
|
test(Concrete2):
|
||||||
|
type _ = seq[int]
|
||||||
|
test(Generic2[int,float]):
|
||||||
|
type _ = seq[int]
|
||||||
|
test(Alias1):
|
||||||
|
type _ = float
|
||||||
|
test(Alias2):
|
||||||
|
type _ = seq[int]
|
||||||
|
test(Alias3):
|
||||||
|
type _ = seq[int]
|
||||||
|
test(Vec[4,float32]):
|
||||||
|
type _ = object
|
||||||
|
arr: array[0..3,float32]
|
||||||
|
test(Vec4[float32]):
|
||||||
|
type _ = object
|
||||||
|
arr: array[0..3,float32]
|
||||||
|
|
||||||
# bug #4862
|
# bug #4862
|
||||||
static:
|
static:
|
||||||
|
|
|
||||||
32
tests/osproc/texecps.nim
Normal file
32
tests/osproc/texecps.nim
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
discard """
|
||||||
|
file: "texecps.nim"
|
||||||
|
output: ""
|
||||||
|
"""
|
||||||
|
|
||||||
|
import osproc, streams, strutils, os
|
||||||
|
|
||||||
|
const NumberOfProcesses = 13
|
||||||
|
|
||||||
|
var gResults {.threadvar.}: seq[string]
|
||||||
|
|
||||||
|
proc execCb(idx: int, p: Process) =
|
||||||
|
let exitCode = p.peekExitCode
|
||||||
|
if exitCode < len(gResults):
|
||||||
|
gResults[exitCode] = p.outputStream.readAll.strip
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
|
||||||
|
if paramCount() == 0:
|
||||||
|
gResults = newSeq[string](NumberOfProcesses)
|
||||||
|
var checks = newSeq[string](NumberOfProcesses)
|
||||||
|
var commands = newSeq[string](NumberOfProcesses)
|
||||||
|
for i in 0..len(commands) - 1:
|
||||||
|
commands[i] = getAppFileName() & " " & $i
|
||||||
|
checks[i] = $i
|
||||||
|
let cres = execProcesses(commands, options = {poStdErrToStdOut},
|
||||||
|
afterRunEvent = execCb)
|
||||||
|
doAssert(cres == len(commands) - 1)
|
||||||
|
doAssert(gResults == checks)
|
||||||
|
else:
|
||||||
|
echo paramStr(1)
|
||||||
|
programResult = parseInt(paramStr(1))
|
||||||
|
|
@ -11,12 +11,19 @@ discard """
|
||||||
sortoutput: true
|
sortoutput: true
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threadpool
|
import threadpool, locks
|
||||||
|
|
||||||
|
var echoLock: Lock
|
||||||
|
initLock echoLock
|
||||||
|
|
||||||
proc f(a: openArray[int]) =
|
proc f(a: openArray[int]) =
|
||||||
for x in a: echo x
|
for x in a:
|
||||||
|
withLock echoLock:
|
||||||
|
echo x
|
||||||
|
|
||||||
proc f(a: int) = echo a
|
proc f(a: int) =
|
||||||
|
withLock echoLock:
|
||||||
|
echo a
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
|
var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ no params call to a
|
||||||
no params call to b
|
no params call to b
|
||||||
100
|
100
|
||||||
one param call to c with 10
|
one param call to c with 10
|
||||||
100'''
|
100
|
||||||
|
0 4'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -23,16 +24,16 @@ type
|
||||||
T2 = object
|
T2 = object
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
proc `.`*(v: T1, f: string): int =
|
template `.`*(v: T1, f: untyped): int =
|
||||||
echo "reading field ", f
|
echo "reading field ", astToStr(f)
|
||||||
return v.x
|
v.x
|
||||||
|
|
||||||
proc `.=`(x: var T1, f: string{lit}, v: int) =
|
template `.=`(t: var T1, f: untyped, v: int) =
|
||||||
echo "assigning ", f, " = ", v
|
echo "assigning ", astToStr(f), " = ", v
|
||||||
x.x = v
|
t.x = v
|
||||||
|
|
||||||
template `.()`(x: T1, f: string, args: varargs[typed]): string =
|
template `.()`(x: T1, f: untyped, args: varargs[typed]): string =
|
||||||
echo "call to ", f
|
echo "call to ", astToStr(f)
|
||||||
"dot call"
|
"dot call"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
@ -47,13 +48,13 @@ echo t.y()
|
||||||
var d = TD(t)
|
var d = TD(t)
|
||||||
assert(not compiles(d.y))
|
assert(not compiles(d.y))
|
||||||
|
|
||||||
proc `.`(v: T2, f: string): int =
|
template `.`(v: T2, f: untyped): int =
|
||||||
echo "no params call to ", f
|
echo "no params call to ", astToStr(f)
|
||||||
return v.x
|
v.x
|
||||||
|
|
||||||
proc `.`*(v: T2, f: string, a: int): int =
|
template `.`*(v: T2, f: untyped, a: int): int =
|
||||||
echo "one param call to ", f, " with ", a
|
echo "one param call to ", astToStr(f), " with ", a
|
||||||
return v.x
|
v.x
|
||||||
|
|
||||||
var tt = T2(x: 100)
|
var tt = T2(x: 100)
|
||||||
|
|
||||||
|
|
@ -63,3 +64,24 @@ echo tt.c(10)
|
||||||
|
|
||||||
assert(not compiles(tt.d("x")))
|
assert(not compiles(tt.d("x")))
|
||||||
assert(not compiles(tt.d(1, 2)))
|
assert(not compiles(tt.d(1, 2)))
|
||||||
|
|
||||||
|
# test simple usage that delegates fields:
|
||||||
|
type
|
||||||
|
Other = object
|
||||||
|
a: int
|
||||||
|
b: string
|
||||||
|
MyObject = object
|
||||||
|
nested: Other
|
||||||
|
x, y: int
|
||||||
|
|
||||||
|
template `.`(x: MyObject; field: untyped): untyped =
|
||||||
|
x.nested.field
|
||||||
|
|
||||||
|
template `.=`(x: MyObject; field, value: untyped) =
|
||||||
|
x.nested.field = value
|
||||||
|
|
||||||
|
var m: MyObject
|
||||||
|
|
||||||
|
m.a = 4
|
||||||
|
m.b = "foo"
|
||||||
|
echo m.x, " ", m.a
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ discard """
|
||||||
file: "tjsonmacro.nim"
|
file: "tjsonmacro.nim"
|
||||||
output: ""
|
output: ""
|
||||||
"""
|
"""
|
||||||
import json, strutils
|
import json, strutils, options, tables
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# Tests inspired by own use case (with some additional tests).
|
# Tests inspired by own use case (with some additional tests).
|
||||||
|
|
@ -247,3 +247,137 @@ when isMainModule:
|
||||||
let jnode = %b
|
let jnode = %b
|
||||||
let data = jnode.to(Bird)
|
let data = jnode.to(Bird)
|
||||||
doAssert data == b
|
doAssert data == b
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
MsgBase = ref object of RootObj
|
||||||
|
name*: string
|
||||||
|
|
||||||
|
MsgChallenge = ref object of MsgBase
|
||||||
|
challenge*: string
|
||||||
|
|
||||||
|
let data = %*{"name": "foo", "challenge": "bar"}
|
||||||
|
let msg = data.to(MsgChallenge)
|
||||||
|
doAssert msg.name == "foo"
|
||||||
|
doAssert msg.challenge == "bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Color = enum Red, Brown
|
||||||
|
Thing = object
|
||||||
|
animal: tuple[fur: bool, legs: int]
|
||||||
|
color: Color
|
||||||
|
|
||||||
|
var j = parseJson("""
|
||||||
|
{"animal":{"fur":true,"legs":6},"color":"Red"}
|
||||||
|
""")
|
||||||
|
|
||||||
|
let parsed = to(j, Thing)
|
||||||
|
doAssert parsed.animal.fur
|
||||||
|
doAssert parsed.animal.legs == 6
|
||||||
|
doAssert parsed.color == Red
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Car = object
|
||||||
|
engine: tuple[name: string, capacity: float]
|
||||||
|
model: string
|
||||||
|
|
||||||
|
let j = """
|
||||||
|
{"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
proc mulTest: JsonNode =
|
||||||
|
i.inc()
|
||||||
|
return parseJson(j)
|
||||||
|
|
||||||
|
let parsed = mulTest().to(Car)
|
||||||
|
doAssert parsed.engine.name == "V8"
|
||||||
|
|
||||||
|
doAssert i == 1
|
||||||
|
|
||||||
|
block:
|
||||||
|
# Option[T] support!
|
||||||
|
type
|
||||||
|
Car1 = object # TODO: Codegen bug when `Car`
|
||||||
|
engine: tuple[name: string, capacity: Option[float]]
|
||||||
|
model: string
|
||||||
|
year: Option[int]
|
||||||
|
|
||||||
|
let noYear = """
|
||||||
|
{"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let noYearParsed = parseJson(noYear)
|
||||||
|
let noYearDeser = to(noYearParsed, Car1)
|
||||||
|
doAssert noYearDeser.engine.capacity == some(5.5)
|
||||||
|
doAssert noYearDeser.year.isNone
|
||||||
|
doAssert noYearDeser.engine.name == "V8"
|
||||||
|
|
||||||
|
# Table[T, Y] support.
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Friend = object
|
||||||
|
name: string
|
||||||
|
age: int
|
||||||
|
|
||||||
|
Dynamic = object
|
||||||
|
name: string
|
||||||
|
friends: Table[string, Friend]
|
||||||
|
|
||||||
|
let data = """
|
||||||
|
{"friends": {
|
||||||
|
"John": {"name": "John", "age": 35},
|
||||||
|
"Elizabeth": {"name": "Elizabeth", "age": 23}
|
||||||
|
}, "name": "Dominik"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let dataParsed = parseJson(data)
|
||||||
|
let dataDeser = to(dataParsed, Dynamic)
|
||||||
|
doAssert dataDeser.name == "Dominik"
|
||||||
|
doAssert dataDeser.friends["John"].age == 35
|
||||||
|
doAssert dataDeser.friends["Elizabeth"].age == 23
|
||||||
|
|
||||||
|
# JsonNode support
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Test = object
|
||||||
|
name: string
|
||||||
|
fallback: JsonNode
|
||||||
|
|
||||||
|
let data = """
|
||||||
|
{"name": "FooBar", "fallback": 56.42}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let dataParsed = parseJson(data)
|
||||||
|
let dataDeser = to(dataParsed, Test)
|
||||||
|
doAssert dataDeser.name == "FooBar"
|
||||||
|
doAssert dataDeser.fallback.kind == JFloat
|
||||||
|
doAssert dataDeser.fallback.getFloat() == 56.42
|
||||||
|
|
||||||
|
# int64, float64 etc support.
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Test1 = object
|
||||||
|
a: int8
|
||||||
|
b: int16
|
||||||
|
c: int32
|
||||||
|
d: int64
|
||||||
|
e: uint8
|
||||||
|
f: uint16
|
||||||
|
g: uint32
|
||||||
|
h: uint64
|
||||||
|
i: float32
|
||||||
|
j: float64
|
||||||
|
|
||||||
|
let data = """
|
||||||
|
{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7,
|
||||||
|
"h": 8, "i": 9.9, "j": 10.10}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let dataParsed = parseJson(data)
|
||||||
|
let dataDeser = to(dataParsed, Test1)
|
||||||
|
doAssert dataDeser.a == 1
|
||||||
|
doAssert dataDeser.f == 6
|
||||||
|
doAssert dataDeser.i == 9.9'f32
|
||||||
18
tests/stdlib/tjsonmacro_reject.nim
Normal file
18
tests/stdlib/tjsonmacro_reject.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
discard """
|
||||||
|
file: "tjsonmacro_reject.nim"
|
||||||
|
line: 11
|
||||||
|
errormsg: "Use a named tuple instead of: (string, float)"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
type
|
||||||
|
Car = object
|
||||||
|
engine: (string, float)
|
||||||
|
model: string
|
||||||
|
|
||||||
|
let j = """
|
||||||
|
{"engine": {"name": "V8", "capacity": 5.5}, model: "Skyline"}
|
||||||
|
"""
|
||||||
|
let parsed = parseJson(j)
|
||||||
|
echo(to(parsed, Car))
|
||||||
21
tests/stdlib/tjsonmacro_reject2.nim
Normal file
21
tests/stdlib/tjsonmacro_reject2.nim
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
discard """
|
||||||
|
file: "tjsonmacro_reject2.nim"
|
||||||
|
line: 10
|
||||||
|
errormsg: "The `to` macro does not support ref objects with cycles."
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
type
|
||||||
|
Misdirection = object
|
||||||
|
cycle: Cycle
|
||||||
|
|
||||||
|
Cycle = ref object
|
||||||
|
foo: string
|
||||||
|
cycle: Misdirection
|
||||||
|
|
||||||
|
let data = """
|
||||||
|
{"cycle": null}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let dataParsed = parseJson(data)
|
||||||
|
let dataDeser = to(dataParsed, Cycle)
|
||||||
|
|
@ -50,6 +50,10 @@ proc test_string_slice() =
|
||||||
s[2..0] = numbers
|
s[2..0] = numbers
|
||||||
doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
|
doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
# bug #6223
|
||||||
|
doAssertRaises(IndexError):
|
||||||
|
discard s[0..999]
|
||||||
|
|
||||||
echo("OK")
|
echo("OK")
|
||||||
|
|
||||||
test_string_slice()
|
test_string_slice()
|
||||||
|
|
|
||||||
5
todo.txt
5
todo.txt
|
|
@ -1,6 +1,9 @@
|
||||||
version 1.0 battle plan
|
version 1.0 battle plan
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
- introduce ``nkStmtListExpr`` for template/macro invokations to produce
|
||||||
|
better stack traces
|
||||||
|
- let 'doAssert' analyse the expressions and produce more helpful output
|
||||||
- fix "high priority" bugs
|
- fix "high priority" bugs
|
||||||
- try to fix as many compiler crashes as reasonable
|
- try to fix as many compiler crashes as reasonable
|
||||||
|
|
||||||
|
|
@ -28,7 +31,6 @@ Not critical for 1.0
|
||||||
- pragmas need 'bindSym' support
|
- pragmas need 'bindSym' support
|
||||||
- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work
|
- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work
|
||||||
reliably with user-defined pragmas
|
reliably with user-defined pragmas
|
||||||
- memory manager: add a measure of fragmentation
|
|
||||||
- we need a magic thisModule symbol
|
- we need a magic thisModule symbol
|
||||||
- optimize 'genericReset'; 'newException' leads to code bloat
|
- optimize 'genericReset'; 'newException' leads to code bloat
|
||||||
|
|
||||||
|
|
@ -49,7 +51,6 @@ Bugs
|
||||||
GC
|
GC
|
||||||
==
|
==
|
||||||
|
|
||||||
- use slightly bigger blocks in the allocator
|
|
||||||
- resizing of strings/sequences could take into account the memory that
|
- resizing of strings/sequences could take into account the memory that
|
||||||
is allocated
|
is allocated
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ proc downloadMingw(): DownloadResult =
|
||||||
let curl = findExe"curl"
|
let curl = findExe"curl"
|
||||||
var cmd: string
|
var cmd: string
|
||||||
if curl.len > 0:
|
if curl.len > 0:
|
||||||
cmd = curl & " --out " & "dist" / mingw & " " & url
|
cmd = quoteShell(curl) & " --out " & "dist" / mingw & " " & url
|
||||||
elif fileExists"bin/nimgrab.exe":
|
elif fileExists"bin/nimgrab.exe":
|
||||||
cmd = "bin/nimgrab.exe " & url & " dist" / mingw
|
cmd = "bin/nimgrab.exe " & url & " dist" / mingw
|
||||||
if cmd.len > 0:
|
if cmd.len > 0:
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ proc initConfigData(c: var TConfigData) =
|
||||||
c.pdf = @[]
|
c.pdf = @[]
|
||||||
c.infile = ""
|
c.infile = ""
|
||||||
c.outdir = ""
|
c.outdir = ""
|
||||||
c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off "
|
c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot "
|
||||||
c.authors = ""
|
c.authors = ""
|
||||||
c.projectTitle = ""
|
c.projectTitle = ""
|
||||||
c.projectName = ""
|
c.projectName = ""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue