Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
commit
1bbab827c4
115 changed files with 1279 additions and 1542 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -57,3 +57,7 @@ dist/
|
||||||
# Private directories and files (IDEs)
|
# Private directories and files (IDEs)
|
||||||
.*/
|
.*/
|
||||||
~*
|
~*
|
||||||
|
|
||||||
|
# testament cruft
|
||||||
|
testresults/
|
||||||
|
test.txt
|
||||||
|
|
|
||||||
30
changelog.md
30
changelog.md
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
### Changes affecting backwards compatibility
|
### Changes affecting backwards compatibility
|
||||||
|
|
||||||
- Removed basic2d/basic3d out of the stdlib and into Nimble packages.
|
|
||||||
These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
|
|
||||||
or another package.
|
|
||||||
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
|
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
|
||||||
arrays of char can! This means ``$`` for arrays can finally exist
|
arrays of char can! This means ``$`` for arrays can finally exist
|
||||||
in ``system.nim`` and do the right thing.
|
in ``system.nim`` and do the right thing.
|
||||||
|
|
@ -16,7 +14,6 @@
|
||||||
module.
|
module.
|
||||||
- The overloading rules changed slightly so that constrained generics are
|
- The overloading rules changed slightly so that constrained generics are
|
||||||
preferred over unconstrained generics. (Bug #6526)
|
preferred over unconstrained generics. (Bug #6526)
|
||||||
- Removed libuv out of the stdlib and into Nimble packages.
|
|
||||||
- It is now possible to forward declare object types so that mutually
|
- It is now possible to forward declare object types so that mutually
|
||||||
recursive types can be created across module boundaries. See
|
recursive types can be created across module boundaries. See
|
||||||
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
|
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
|
||||||
|
|
@ -40,8 +37,6 @@
|
||||||
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
|
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
|
||||||
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
|
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
|
||||||
what to return if the environment variable does not exist.
|
what to return if the environment variable does not exist.
|
||||||
- Removed PDCurses wrapper from the stdlib and published it as a separate
|
|
||||||
Nimble package.
|
|
||||||
- Bodies of ``for`` loops now get their own scope:
|
- Bodies of ``for`` loops now get their own scope:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -74,3 +69,26 @@ This now needs to be written as:
|
||||||
var a = -5
|
var a = -5
|
||||||
for i in a..b:
|
for i in a..b:
|
||||||
echo i
|
echo i
|
||||||
|
|
||||||
|
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
|
||||||
|
precision digits. The previous ``precision = 0`` behavior (default formatting)
|
||||||
|
is now available via ``precision = -1``.
|
||||||
|
- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
|
||||||
|
the documentation generator. The old version 1 can still be accessed
|
||||||
|
via the new ``nim doc0`` command.
|
||||||
|
- Added ``system.getStackTraceEntries`` that allows you to access the stack
|
||||||
|
trace in a structured manner without string parsing.
|
||||||
|
- Added ``sequtils.mapLiterals`` for easier construction of array and tuple
|
||||||
|
literals.
|
||||||
|
- Added ``parseutils.parseSaturatedNatural``.
|
||||||
|
- Moved from stdlib into Nimble packages:
|
||||||
|
- [``basic2d``](https://github.com/nim-lang/basic2d)
|
||||||
|
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
||||||
|
- [``basic3d``](https://github.com/nim-lang/basic3d)
|
||||||
|
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
||||||
|
- [``gentabs``](https://github.com/lcrees/gentabs)
|
||||||
|
- [``libuv``](https://github.com/lcrees/libuv)
|
||||||
|
- [``numeric``](https://github.com/lcrees/polynumeric)
|
||||||
|
- [``poly``](https://github.com/lcrees/polynumeric)
|
||||||
|
- [``pdcurses``](https://github.com/lcrees/pdcurses)
|
||||||
|
- [``romans``](https://github.com/lcrees/romans)
|
||||||
|
|
|
||||||
|
|
@ -798,8 +798,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||||
|
|
||||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
|
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
|
||||||
|
|
||||||
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
|
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
||||||
origTy: PType) =
|
|
||||||
var test, u, v: TLoc
|
var test, u, v: TLoc
|
||||||
for i in countup(1, sonsLen(e) - 1):
|
for i in countup(1, sonsLen(e) - 1):
|
||||||
var it = e.sons[i]
|
var it = e.sons[i]
|
||||||
|
|
@ -811,12 +810,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
|
||||||
assert(disc.kind == nkSym)
|
assert(disc.kind == nkSym)
|
||||||
initLoc(test, locNone, it, OnStack)
|
initLoc(test, locNone, it, OnStack)
|
||||||
initLocExpr(p, it.sons[1], u)
|
initLocExpr(p, it.sons[1], u)
|
||||||
var o = obj
|
|
||||||
let d = lookupFieldAgain(p, origTy, disc.sym, o)
|
|
||||||
initLoc(v, locExpr, disc, OnUnknown)
|
initLoc(v, locExpr, disc, OnUnknown)
|
||||||
v.r = o
|
v.r = obj
|
||||||
v.r.add(".")
|
v.r.add(".")
|
||||||
v.r.add(d.loc.r)
|
v.r.add(disc.sym.loc.r)
|
||||||
genInExprAux(p, it, u, v, test)
|
genInExprAux(p, it, u, v, test)
|
||||||
let id = nodeTableTestOrSet(p.module.dataCache,
|
let id = nodeTableTestOrSet(p.module.dataCache,
|
||||||
newStrNode(nkStrLit, field.name.s), p.module.labels)
|
newStrNode(nkStrLit, field.name.s), p.module.labels)
|
||||||
|
|
@ -842,7 +839,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
||||||
if field.loc.r == nil:
|
if field.loc.r == nil:
|
||||||
internalError(e.info, "genCheckedRecordField") # generate the checks:
|
internalError(e.info, "genCheckedRecordField") # generate the checks:
|
||||||
genFieldCheck(p, e, r, field, ty)
|
genFieldCheck(p, e, r, field)
|
||||||
add(r, rfmt(nil, ".$1", field.loc.r))
|
add(r, rfmt(nil, ".$1", field.loc.r))
|
||||||
putIntoDest(p, d, e.sons[0], r, a.storage)
|
putIntoDest(p, d, e.sons[0], r, a.storage)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1226,7 +1223,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
if field.loc.r == nil: fillObjectFields(p.module, ty)
|
||||||
if field.loc.r == nil: internalError(e.info, "genObjConstr")
|
if field.loc.r == nil: internalError(e.info, "genObjConstr")
|
||||||
if it.len == 3 and optFieldCheck in p.options:
|
if it.len == 3 and optFieldCheck in p.options:
|
||||||
genFieldCheck(p, it.sons[2], r, field, ty)
|
genFieldCheck(p, it.sons[2], r, field)
|
||||||
add(tmp2.r, ".")
|
add(tmp2.r, ".")
|
||||||
add(tmp2.r, field.loc.r)
|
add(tmp2.r, field.loc.r)
|
||||||
tmp2.k = locTemp
|
tmp2.k = locTemp
|
||||||
|
|
@ -1645,8 +1642,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||||
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
||||||
else:
|
else:
|
||||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
|
||||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
# C++ does not like direct casts from pointer to shorter integral types
|
||||||
|
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
||||||
|
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
||||||
|
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||||
|
else:
|
||||||
|
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||||
|
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||||
|
|
||||||
proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
||||||
const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
|
const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
|
||||||
|
|
@ -2275,7 +2278,13 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
|
||||||
result = "{{$1}}" % [genTypeInfo(p.module, t, info)]
|
result = "{{$1}}" % [genTypeInfo(p.module, t, info)]
|
||||||
else:
|
else:
|
||||||
result = rope"{}"
|
result = rope"{}"
|
||||||
of tyArray, tyTuple: result = rope"{}"
|
of tyTuple:
|
||||||
|
result = rope"{"
|
||||||
|
for i in 0 ..< typ.len:
|
||||||
|
if i > 0: result.add ", "
|
||||||
|
result.add getDefaultValue(p, typ.sons[i], info)
|
||||||
|
result.add "}"
|
||||||
|
of tyArray: result = rope"{}"
|
||||||
of tySet:
|
of tySet:
|
||||||
if mapType(t) == ctArray: result = rope"{}"
|
if mapType(t) == ctArray: result = rope"{}"
|
||||||
else: result = rope"0"
|
else: result = rope"0"
|
||||||
|
|
|
||||||
|
|
@ -896,14 +896,15 @@ proc addIntTypes(result: var Rope) {.inline.} =
|
||||||
platform.CPU[targetCPU].intSize.rope])
|
platform.CPU[targetCPU].intSize.rope])
|
||||||
|
|
||||||
proc getCopyright(cfile: Cfile): Rope =
|
proc getCopyright(cfile: Cfile): Rope =
|
||||||
|
const copyrightYear = "2017"
|
||||||
if optCompileOnly in gGlobalOptions:
|
if optCompileOnly in gGlobalOptions:
|
||||||
result = ("/* Generated by Nim Compiler v$1 */$N" &
|
result = ("/* Generated by Nim Compiler v$1 */$N" &
|
||||||
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
|
"/* (c) " & copyrightYear & " Andreas Rumpf */$N" &
|
||||||
"/* The generated code is subject to the original license. */$N") %
|
"/* The generated code is subject to the original license. */$N") %
|
||||||
[rope(VersionAsString)]
|
[rope(VersionAsString)]
|
||||||
else:
|
else:
|
||||||
result = ("/* Generated by Nim Compiler v$1 */$N" &
|
result = ("/* Generated by Nim Compiler v$1 */$N" &
|
||||||
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
|
"/* (c) " & copyrightYear & " Andreas Rumpf */$N" &
|
||||||
"/* The generated code is subject to the original license. */$N" &
|
"/* The generated code is subject to the original license. */$N" &
|
||||||
"/* Compiled for: $2, $3, $4 */$N" &
|
"/* Compiled for: $2, $3, $4 */$N" &
|
||||||
"/* Command for C compiler:$n $5 */$N") %
|
"/* Command for C compiler:$n $5 */$N") %
|
||||||
|
|
@ -1292,7 +1293,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
|
||||||
|
|
||||||
proc writeHeader(m: BModule) =
|
proc writeHeader(m: BModule) =
|
||||||
var result = ("/* Generated by Nim Compiler v$1 */$N" &
|
var result = ("/* Generated by Nim Compiler v$1 */$N" &
|
||||||
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
|
"/* (c) 2017 Andreas Rumpf */$N" &
|
||||||
"/* The generated code is subject to the original license. */$N") %
|
"/* The generated code is subject to the original license. */$N") %
|
||||||
[rope(VersionAsString)]
|
[rope(VersionAsString)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
|
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
|
||||||
"Copyright (c) 2006-" & CompileDate.substr(0, 3) & " by Andreas Rumpf\n"
|
"Copyright (c) 2006-2017 by Andreas Rumpf\n"
|
||||||
|
|
||||||
const
|
const
|
||||||
Usage = slurp"../doc/basicopt.txt".replace("//", "")
|
Usage = slurp"../doc/basicopt.txt".replace("//", "")
|
||||||
|
|
|
||||||
|
|
@ -608,6 +608,52 @@ proc generateJson*(d: PDoc, n: PNode) =
|
||||||
generateJson(d, lastSon(n.sons[0]))
|
generateJson(d, lastSon(n.sons[0]))
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
|
||||||
|
var
|
||||||
|
name = getName(d, nameNode)
|
||||||
|
|
||||||
|
result = name & "\n"
|
||||||
|
|
||||||
|
proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
|
||||||
|
case n.kind
|
||||||
|
of nkCommentStmt:
|
||||||
|
if n.comment != nil and startsWith(n.comment, "##"):
|
||||||
|
let stripped = n.comment.substr(2).strip
|
||||||
|
r.add stripped
|
||||||
|
of nkProcDef:
|
||||||
|
when useEffectSystem: documentRaises(n)
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skProc)
|
||||||
|
of nkFuncDef:
|
||||||
|
when useEffectSystem: documentRaises(n)
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skFunc)
|
||||||
|
of nkMethodDef:
|
||||||
|
when useEffectSystem: documentRaises(n)
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skMethod)
|
||||||
|
of nkIteratorDef:
|
||||||
|
when useEffectSystem: documentRaises(n)
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skIterator)
|
||||||
|
of nkMacroDef:
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skMacro)
|
||||||
|
of nkTemplateDef:
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skTemplate)
|
||||||
|
of nkConverterDef:
|
||||||
|
when useEffectSystem: documentRaises(n)
|
||||||
|
r.add genTagsItem(d, n, n.sons[namePos], skConverter)
|
||||||
|
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
if n.sons[i].kind != nkCommentStmt:
|
||||||
|
# order is always 'type var let const':
|
||||||
|
r.add genTagsItem(d, n.sons[i], n.sons[i].sons[0],
|
||||||
|
succ(skType, ord(n.kind)-ord(nkTypeSection)))
|
||||||
|
of nkStmtList:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
generateTags(d, n.sons[i], r)
|
||||||
|
of nkWhenStmt:
|
||||||
|
# generate documentation for the first branch only:
|
||||||
|
if not checkForFalse(n.sons[0].sons[0]):
|
||||||
|
generateTags(d, lastSon(n.sons[0]), r)
|
||||||
|
else: discard
|
||||||
|
|
||||||
proc genSection(d: PDoc, kind: TSymKind) =
|
proc genSection(d: PDoc, kind: TSymKind) =
|
||||||
const sectionNames: array[skModule..skTemplate, string] = [
|
const sectionNames: array[skModule..skTemplate, string] = [
|
||||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
|
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
|
||||||
|
|
@ -745,6 +791,21 @@ proc commandJson*() =
|
||||||
#echo getOutFile(gProjectFull, JsonExt)
|
#echo getOutFile(gProjectFull, JsonExt)
|
||||||
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
|
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
|
||||||
|
|
||||||
|
proc commandTags*() =
|
||||||
|
var ast = parseFile(gProjectMainIdx, newIdentCache())
|
||||||
|
if ast == nil: return
|
||||||
|
var d = newDocumentor(gProjectFull, options.gConfigVars)
|
||||||
|
d.hasToc = true
|
||||||
|
var
|
||||||
|
content: Rope
|
||||||
|
generateTags(d, ast, content)
|
||||||
|
|
||||||
|
if optStdout in gGlobalOptions:
|
||||||
|
writeRope(stdout, content)
|
||||||
|
else:
|
||||||
|
#echo getOutFile(gProjectFull, TagsExt)
|
||||||
|
writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false)
|
||||||
|
|
||||||
proc commandBuildIndex*() =
|
proc commandBuildIndex*() =
|
||||||
var content = mergeIndexes(gProjectFull).rope
|
var content = mergeIndexes(gProjectFull).rope
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ proc isLet(n: PNode): bool =
|
||||||
|
|
||||||
proc isVar(n: PNode): bool =
|
proc isVar(n: PNode): bool =
|
||||||
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
|
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
|
||||||
{sfGlobal, sfAddrTaken} * n.sym.flags == {}
|
{sfAddrTaken} * n.sym.flags == {}
|
||||||
|
|
||||||
proc isLetLocation(m: PNode, isApprox: bool): bool =
|
proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||||
# consider: 'n[].kind' --> we really need to support 1 deref op even if this
|
# consider: 'n[].kind' --> we really need to support 1 deref op even if this
|
||||||
|
|
@ -768,8 +768,10 @@ macro `=~`(x: PNode, pat: untyped): bool =
|
||||||
|
|
||||||
var conds = newTree(nnkBracket)
|
var conds = newTree(nnkBracket)
|
||||||
m(x, pat, conds)
|
m(x, pat, conds)
|
||||||
result = nestList(!"and", conds)
|
when declared(macros.toNimIdent):
|
||||||
|
result = nestList(toNimIdent"and", conds)
|
||||||
|
else:
|
||||||
|
result = nestList(!"and", conds)
|
||||||
|
|
||||||
proc isMinusOne(n: PNode): bool =
|
proc isMinusOne(n: PNode): bool =
|
||||||
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
|
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
|
||||||
|
|
|
||||||
|
|
@ -376,8 +376,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
||||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
||||||
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
||||||
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
|
["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
|
||||||
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
|
["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
|
||||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
||||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
||||||
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
||||||
|
|
@ -444,8 +444,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
||||||
["", "", "$1", "$1"], # ToFloat
|
["", "", "$1", "$1"], # ToFloat
|
||||||
["", "", "$1", "$1"], # ToBiggestFloat
|
["", "", "$1", "$1"], # ToBiggestFloat
|
||||||
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
|
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
|
||||||
["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
|
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
|
||||||
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
|
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
|
||||||
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
|
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
|
||||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
||||||
|
|
|
||||||
|
|
@ -186,12 +186,12 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
|
||||||
of "php":
|
of "php":
|
||||||
gCmd = cmdCompileToPHP
|
gCmd = cmdCompileToPHP
|
||||||
commandCompileToJS(graph, cache)
|
commandCompileToJS(graph, cache)
|
||||||
of "doc":
|
of "doc0":
|
||||||
wantMainModule()
|
wantMainModule()
|
||||||
gCmd = cmdDoc
|
gCmd = cmdDoc
|
||||||
loadConfigs(DocConfig, cache)
|
loadConfigs(DocConfig, cache)
|
||||||
commandDoc()
|
commandDoc()
|
||||||
of "doc2":
|
of "doc2", "doc":
|
||||||
gCmd = cmdDoc
|
gCmd = cmdDoc
|
||||||
loadConfigs(DocConfig, cache)
|
loadConfigs(DocConfig, cache)
|
||||||
defineSymbol("nimdoc")
|
defineSymbol("nimdoc")
|
||||||
|
|
@ -217,6 +217,13 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
|
||||||
wantMainModule()
|
wantMainModule()
|
||||||
defineSymbol("nimdoc")
|
defineSymbol("nimdoc")
|
||||||
commandDoc2(graph, cache, true)
|
commandDoc2(graph, cache, true)
|
||||||
|
of "ctags":
|
||||||
|
wantMainModule()
|
||||||
|
gCmd = cmdDoc
|
||||||
|
loadConfigs(DocConfig, cache)
|
||||||
|
wantMainModule()
|
||||||
|
defineSymbol("nimdoc")
|
||||||
|
commandTags()
|
||||||
of "buildindex":
|
of "buildindex":
|
||||||
gCmd = cmdDoc
|
gCmd = cmdDoc
|
||||||
loadConfigs(DocConfig, cache)
|
loadConfigs(DocConfig, cache)
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ const
|
||||||
RodExt* = "rod"
|
RodExt* = "rod"
|
||||||
HtmlExt* = "html"
|
HtmlExt* = "html"
|
||||||
JsonExt* = "json"
|
JsonExt* = "json"
|
||||||
|
TagsExt* = "tags"
|
||||||
TexExt* = "tex"
|
TexExt* = "tex"
|
||||||
IniExt* = "ini"
|
IniExt* = "ini"
|
||||||
DefaultConfig* = "nim.cfg"
|
DefaultConfig* = "nim.cfg"
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Plugin support for the Nim compiler. Right now they
|
## Plugin support for the Nim compiler. Right now plugins
|
||||||
## need to be build with the compiler, no DLL support.
|
## need to be built with the compiler only: plugins using
|
||||||
|
## DLLs or the FFI will not work.
|
||||||
|
|
||||||
import ast, semdata, idents
|
import ast, semdata, idents
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -606,13 +606,13 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
if a == nil: return nil
|
if a == nil: return nil
|
||||||
result.sons[i] = a
|
result.sons[i] = a
|
||||||
incl(result.flags, nfAllConst)
|
incl(result.flags, nfAllConst)
|
||||||
of nkObjConstr:
|
#of nkObjConstr:
|
||||||
result = copyTree(n)
|
# result = copyTree(n)
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
# for i in countup(1, sonsLen(n) - 1):
|
||||||
var a = getConstExpr(m, n.sons[i].sons[1])
|
# var a = getConstExpr(m, n.sons[i].sons[1])
|
||||||
if a == nil: return nil
|
# if a == nil: return nil
|
||||||
result.sons[i].sons[1] = a
|
# result.sons[i].sons[1] = a
|
||||||
incl(result.flags, nfAllConst)
|
# incl(result.flags, nfAllConst)
|
||||||
of nkPar:
|
of nkPar:
|
||||||
# tuple constructor
|
# tuple constructor
|
||||||
result = copyTree(n)
|
result = copyTree(n)
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
|
||||||
of "not":
|
of "not":
|
||||||
return typeWithSonsResult(tyNot, @[operand])
|
return typeWithSonsResult(tyNot, @[operand])
|
||||||
of "name":
|
of "name":
|
||||||
result = newStrNode(nkStrLit, operand.typeToString(preferName))
|
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
||||||
result.typ = newType(tyString, context)
|
result.typ = newType(tyString, context)
|
||||||
result.info = traitCall.info
|
result.info = traitCall.info
|
||||||
of "arity":
|
of "arity":
|
||||||
|
|
|
||||||
|
|
@ -1420,9 +1420,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
else: result = semGeneric(c, n, s, prev)
|
else: result = semGeneric(c, n, s, prev)
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
let typeExpr = semExpr(c, n)
|
let typeExpr = semExpr(c, n)
|
||||||
if typeExpr.typ.kind == tyFromExpr:
|
if typeExpr.typ.isNil:
|
||||||
return typeExpr.typ
|
localError(n.info, "object constructor needs an object type;" &
|
||||||
if typeExpr.typ.kind != tyTypeDesc:
|
" for named arguments use '=' instead of ':'")
|
||||||
|
result = errorType(c)
|
||||||
|
elif typeExpr.typ.kind == tyFromExpr:
|
||||||
|
result = typeExpr.typ
|
||||||
|
elif typeExpr.typ.kind != tyTypeDesc:
|
||||||
localError(n.info, errTypeExpected)
|
localError(n.info, errTypeExpected)
|
||||||
result = errorType(c)
|
result = errorType(c)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1385,7 +1385,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||||
# XXX: This is very hacky. It should be moved back into liftTypeParam
|
# XXX: This is very hacky. It should be moved back into liftTypeParam
|
||||||
if x.kind in {tyGenericInst, tyArray} and
|
if x.kind in {tyGenericInst, tyArray} and
|
||||||
c.calleeSym != nil and
|
c.calleeSym != nil and
|
||||||
c.calleeSym.kind in {skProc, skFunc}:
|
c.calleeSym.kind in {skProc, skFunc} and c.call != nil:
|
||||||
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
||||||
return typeRel(c, inst, a)
|
return typeRel(c, inst, a)
|
||||||
|
|
||||||
|
|
@ -1613,7 +1613,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||||
result = isNone
|
result = isNone
|
||||||
if result != isNone: put(c, f, aOrig)
|
if result != isNone: put(c, f, aOrig)
|
||||||
elif aOrig.n != nil:
|
elif aOrig.n != nil and aOrig.n.typ != nil:
|
||||||
result = typeRel(c, f.lastSon, aOrig.n.typ)
|
result = typeRel(c, f.lastSon, aOrig.n.typ)
|
||||||
if result != isNone:
|
if result != isNone:
|
||||||
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||||
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
||||||
n.sons[0].sons[0] = m.sons[0]
|
n.sons[0].sons[0] = m.sons[0]
|
||||||
result = PTransNode(n.sons[0])
|
result = PTransNode(n.sons[0])
|
||||||
if n.typ.kind != tyOpenArray:
|
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||||
PNode(result).typ = n.typ
|
PNode(result).typ = n.typ
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
var m = n.sons[0].sons[1]
|
var m = n.sons[0].sons[1]
|
||||||
|
|
@ -373,13 +373,13 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||||
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
|
||||||
n.sons[0].sons[1] = m.sons[0]
|
n.sons[0].sons[1] = m.sons[0]
|
||||||
result = PTransNode(n.sons[0])
|
result = PTransNode(n.sons[0])
|
||||||
if n.typ.kind != tyOpenArray:
|
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||||
PNode(result).typ = n.typ
|
PNode(result).typ = n.typ
|
||||||
else:
|
else:
|
||||||
if n.sons[0].kind == a or n.sons[0].kind == b:
|
if n.sons[0].kind == a or n.sons[0].kind == b:
|
||||||
# addr ( deref ( x )) --> x
|
# addr ( deref ( x )) --> x
|
||||||
result = PTransNode(n.sons[0].sons[0])
|
result = PTransNode(n.sons[0].sons[0])
|
||||||
if n.typ.kind != tyOpenArray:
|
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||||
PNode(result).typ = n.typ
|
PNode(result).typ = n.typ
|
||||||
|
|
||||||
proc generateThunk(prc: PNode, dest: PType): PNode =
|
proc generateThunk(prc: PNode, dest: PType): PNode =
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ import
|
||||||
|
|
||||||
type
|
type
|
||||||
TPreferedDesc* = enum
|
TPreferedDesc* = enum
|
||||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg,
|
||||||
|
preferTypeName
|
||||||
|
|
||||||
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||||
template `$`*(typ: PType): string = typeToString(typ)
|
template `$`*(typ: PType): string = typeToString(typ)
|
||||||
|
|
@ -394,7 +395,7 @@ const
|
||||||
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
|
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
|
||||||
"void"]
|
"void"]
|
||||||
|
|
||||||
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
|
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg}
|
||||||
|
|
||||||
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
|
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
|
||||||
tc.sons.safeAdd concrete
|
tc.sons.safeAdd concrete
|
||||||
|
|
@ -420,7 +421,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
sfAnon notin t.sym.flags:
|
sfAnon notin t.sym.flags:
|
||||||
if t.kind == tyInt and isIntLit(t):
|
if t.kind == tyInt and isIntLit(t):
|
||||||
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||||
elif prefer == preferName or t.sym.owner.isNil:
|
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
|
||||||
result = t.sym.name.s
|
result = t.sym.name.s
|
||||||
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
|
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
|
||||||
result.add ": "
|
result.add ": "
|
||||||
|
|
@ -518,7 +519,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
result = "openarray[" & typeToString(t.sons[0]) & ']'
|
result = "openarray[" & typeToString(t.sons[0]) & ']'
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
result = "distinct " & typeToString(t.sons[0],
|
result = "distinct " & typeToString(t.sons[0],
|
||||||
if prefer == preferModuleInfo: preferModuleInfo else: preferName)
|
if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
# we iterate over t.sons here, because t.n may be nil
|
# we iterate over t.sons here, because t.n may be nil
|
||||||
if t.n != nil:
|
if t.n != nil:
|
||||||
|
|
@ -532,7 +533,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
elif sonsLen(t) == 0:
|
elif sonsLen(t) == 0:
|
||||||
result = "tuple[]"
|
result = "tuple[]"
|
||||||
else:
|
else:
|
||||||
result = "("
|
if prefer == preferTypeName: result = "("
|
||||||
|
else: result = "tuple of ("
|
||||||
for i in countup(0, sonsLen(t) - 1):
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
add(result, typeToString(t.sons[i]))
|
add(result, typeToString(t.sons[i]))
|
||||||
if i < sonsLen(t) - 1: add(result, ", ")
|
if i < sonsLen(t) - 1: add(result, ", ")
|
||||||
|
|
|
||||||
|
|
@ -1773,8 +1773,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
|
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
|
||||||
of nkIfStmt, nkIfExpr: genIf(c, n, dest)
|
of nkIfStmt, nkIfExpr: genIf(c, n, dest)
|
||||||
of nkWhenStmt:
|
of nkWhenStmt:
|
||||||
# This is "when nimvm" node. Chose the first branch.
|
# This is "when nimvm" node. Chose the first branch.
|
||||||
gen(c, n.sons[0].sons[1], dest)
|
gen(c, n.sons[0].sons[1], dest)
|
||||||
of nkCaseStmt: genCase(c, n, dest)
|
of nkCaseStmt: genCase(c, n, dest)
|
||||||
of nkWhileStmt:
|
of nkWhileStmt:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
|
|
@ -1810,7 +1810,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
genVarSection(c, n)
|
genVarSection(c, n)
|
||||||
of declarativeDefs:
|
of declarativeDefs, nkMacroDef:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
of nkLambdaKinds:
|
of nkLambdaKinds:
|
||||||
#let s = n.sons[namePos].sym
|
#let s = n.sons[namePos].sym
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ Advanced commands:
|
||||||
//rst2html convert a reStructuredText file to HTML
|
//rst2html convert a reStructuredText file to HTML
|
||||||
//rst2tex convert a reStructuredText file to TeX
|
//rst2tex convert a reStructuredText file to TeX
|
||||||
//jsondoc extract the documentation to a json file
|
//jsondoc extract the documentation to a json file
|
||||||
//jsondoc2 extract documentation to a json file (uses doc2)
|
//jsondoc2 extract the documentation to a json file (uses doc2)
|
||||||
|
//ctags create a tags file
|
||||||
//buildIndex build an index for the whole documentation
|
//buildIndex build an index for the whole documentation
|
||||||
//run run the project (with Tiny C backend; buggy!)
|
//run run the project (with Tiny C backend; buggy!)
|
||||||
//genDepend generate a DOT file containing the
|
//genDepend generate a DOT file containing the
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
Command:
|
Command:
|
||||||
//compile, c compile project with default code generator (C)
|
//compile, c compile project with default code generator (C)
|
||||||
//doc generate the documentation for inputfile
|
//doc generate the documentation for inputfile
|
||||||
//doc2 generate the documentation for inputfile
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
arguments are passed to the program being run (if --run option is selected)
|
arguments are passed to the program being run (if --run option is selected)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ Core
|
||||||
* `cpuinfo <cpuinfo.html>`_
|
* `cpuinfo <cpuinfo.html>`_
|
||||||
This module implements procs to determine the number of CPUs / cores.
|
This module implements procs to determine the number of CPUs / cores.
|
||||||
|
|
||||||
|
|
||||||
Collections and algorithms
|
Collections and algorithms
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|
@ -182,6 +181,12 @@ Generic Operating System Services
|
||||||
This module implements asynchronous file reading and writing using
|
This module implements asynchronous file reading and writing using
|
||||||
``asyncdispatch``.
|
``asyncdispatch``.
|
||||||
|
|
||||||
|
* `distros <distros.html>`_
|
||||||
|
This module implements the basics for OS distribution ("distro") detection and the OS's native package manager.
|
||||||
|
Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum
|
||||||
|
that is useful for writing platform specific code.
|
||||||
|
|
||||||
|
|
||||||
Math libraries
|
Math libraries
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,26 +9,26 @@ The following example shows a generic binary tree can be modelled:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
BinaryTreeObj[T] = object # BinaryTreeObj is a generic type with
|
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
||||||
# with generic param ``T``
|
# generic param ``T``
|
||||||
le, ri: BinaryTree[T] # left and right subtrees; may be nil
|
le, ri: BinaryTree[T] # left and right subtrees; may be nil
|
||||||
data: T # the data stored in a node
|
data: T # the data stored in a node
|
||||||
BinaryTree[T] = ref BinaryTreeObj[T] # a shorthand for notational convenience
|
|
||||||
|
|
||||||
proc newNode[T](data: T): BinaryTree[T] = # constructor for a node
|
proc newNode*[T](data: T): BinaryTree[T] =
|
||||||
|
# constructor for a node
|
||||||
new(result)
|
new(result)
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
proc add[T](root: var BinaryTree[T], n: BinaryTree[T]) =
|
proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
|
||||||
|
# insert a node into the tree
|
||||||
if root == nil:
|
if root == nil:
|
||||||
root = n
|
root = n
|
||||||
else:
|
else:
|
||||||
var it = root
|
var it = root
|
||||||
while it != nil:
|
while it != nil:
|
||||||
var c = cmp(it.data, n.data) # compare the data items; uses
|
# compare the data items; uses the generic ``cmp`` proc
|
||||||
# the generic ``cmp`` proc that works for
|
# that works for any type that has a ``==`` and ``<`` operator
|
||||||
# any type that has a ``==`` and ``<``
|
var c = cmp(it.data, n.data)
|
||||||
# operator
|
|
||||||
if c < 0:
|
if c < 0:
|
||||||
if it.le == nil:
|
if it.le == nil:
|
||||||
it.le = n
|
it.le = n
|
||||||
|
|
@ -40,20 +40,28 @@ The following example shows a generic binary tree can be modelled:
|
||||||
return
|
return
|
||||||
it = it.ri
|
it = it.ri
|
||||||
|
|
||||||
iterator inorder[T](root: BinaryTree[T]): T =
|
proc add*[T](root: var BinaryTree[T], data: T) =
|
||||||
# inorder traversal of a binary tree
|
# convenience proc:
|
||||||
# recursive iterators are not yet implemented, so this does not work in
|
add(root, newNode(data))
|
||||||
# the current compiler!
|
|
||||||
if root.le != nil: yield inorder(root.le)
|
iterator preorder*[T](root: BinaryTree[T]): T =
|
||||||
yield root.data
|
# Preorder traversal of a binary tree.
|
||||||
if root.ri != nil: yield inorder(root.ri)
|
# Since recursive iterators are not yet implemented,
|
||||||
|
# this uses an explicit stack (which is more efficient anyway):
|
||||||
|
var stack: seq[BinaryTree[T]] = @[root]
|
||||||
|
while stack.len > 0:
|
||||||
|
var n = stack.pop()
|
||||||
|
while n != nil:
|
||||||
|
yield n.data
|
||||||
|
add(stack, n.ri) # push right subtree onto the stack
|
||||||
|
n = n.le # and follow the left pointer
|
||||||
|
|
||||||
var
|
var
|
||||||
root: BinaryTree[string] # instantiate a BinaryTree with the type string
|
root: BinaryTree[string] # instantiate a BinaryTree with ``string``
|
||||||
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and
|
add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
|
||||||
add(root, newNode("world")) # ``add``
|
add(root, "world") # instantiates the second ``add`` proc
|
||||||
for str in inorder(root):
|
for str in preorder(root):
|
||||||
writeLine(stdout, str)
|
stdout.writeLine(str)
|
||||||
|
|
||||||
|
|
||||||
Is operator
|
Is operator
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,14 @@ collector to not consider objects of this type as part of a cycle:
|
||||||
left, right: Node
|
left, right: Node
|
||||||
data: string
|
data: string
|
||||||
|
|
||||||
|
Or if we directly use a ref object:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
type
|
||||||
|
Node = ref object {.acyclic, final.}
|
||||||
|
left, right: Node
|
||||||
|
data: string
|
||||||
|
|
||||||
In the example a tree structure is declared with the ``Node`` type. Note that
|
In the example a tree structure is declared with the ``Node`` type. Note that
|
||||||
the type definition is recursive and the GC has to assume that objects of
|
the type definition is recursive and the GC has to assume that objects of
|
||||||
this type may form a cyclic graph. The ``acyclic`` pragma passes the
|
this type may form a cyclic graph. The ``acyclic`` pragma passes the
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type # example demonstrating mutually recursive types
|
type # example demonstrating mutually recursive types
|
||||||
Node = ref NodeObj # a traced pointer to a NodeObj
|
Node = ref object # an object managed by the garbage collector (ref)
|
||||||
NodeObj = object
|
|
||||||
le, ri: Node # left and right subtrees
|
le, ri: Node # left and right subtrees
|
||||||
sym: ref Sym # leaves contain a reference to a Sym
|
sym: ref Sym # leaves contain a reference to a Sym
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1511,8 +1511,7 @@ operators perform implicit dereferencing operations for reference types:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Node = ref NodeObj
|
Node = ref object
|
||||||
NodeObj = object
|
|
||||||
le, ri: Node
|
le, ri: Node
|
||||||
data: int
|
data: int
|
||||||
var
|
var
|
||||||
|
|
|
||||||
17
doc/tut2.rst
17
doc/tut2.rst
|
|
@ -104,15 +104,14 @@ Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
Node = ref NodeObj # a traced reference to a NodeObj
|
Node = ref object # a reference to an object with the following field:
|
||||||
NodeObj = object
|
|
||||||
le, ri: Node # left and right subtrees
|
le, ri: Node # left and right subtrees
|
||||||
sym: ref Sym # leaves contain a reference to a Sym
|
sym: ref Sym # leaves contain a reference to a Sym
|
||||||
|
|
||||||
Sym = object # a symbol
|
Sym = object # a symbol
|
||||||
name: string # the symbol's name
|
name: string # the symbol's name
|
||||||
line: int # the line the symbol was declared in
|
line: int # the line the symbol was declared in
|
||||||
code: Node # the symbol's abstract syntax tree
|
code: Node # the symbol's abstract syntax tree
|
||||||
|
|
||||||
|
|
||||||
Type conversions
|
Type conversions
|
||||||
|
|
@ -155,8 +154,7 @@ An example:
|
||||||
nkAdd, # an addition
|
nkAdd, # an addition
|
||||||
nkSub, # a subtraction
|
nkSub, # a subtraction
|
||||||
nkIf # an if statement
|
nkIf # an if statement
|
||||||
Node = ref NodeObj
|
Node = ref object
|
||||||
NodeObj = object
|
|
||||||
case kind: NodeKind # the ``kind`` field is the discriminator
|
case kind: NodeKind # the ``kind`` field is the discriminator
|
||||||
of nkInt: intVal: int
|
of nkInt: intVal: int
|
||||||
of nkFloat: floatVal: float
|
of nkFloat: floatVal: float
|
||||||
|
|
@ -482,11 +480,10 @@ containers:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
BinaryTreeObj[T] = object # BinaryTree is a generic type with
|
BinaryTree*[T] = ref object # BinaryTree is a generic type with
|
||||||
# with generic param ``T``
|
# generic param ``T``
|
||||||
le, ri: BinaryTree[T] # left and right subtrees; may be nil
|
le, ri: BinaryTree[T] # left and right subtrees; may be nil
|
||||||
data: T # the data stored in a node
|
data: T # the data stored in a node
|
||||||
BinaryTree*[T] = ref BinaryTreeObj[T] # type that is exported
|
|
||||||
|
|
||||||
proc newNode*[T](data: T): BinaryTree[T] =
|
proc newNode*[T](data: T): BinaryTree[T] =
|
||||||
# constructor for a node
|
# constructor for a node
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
<int key="IBUITag">2</int>
|
<int key="IBUITag">2</int>
|
||||||
<bool key="IBUIUserInteractionEnabled">NO</bool>
|
<bool key="IBUIUserInteractionEnabled">NO</bool>
|
||||||
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
||||||
<string key="IBUIText">Nimrod Crossplatform Calculator</string>
|
<string key="IBUIText">Nim Crossplatform Calculator</string>
|
||||||
<object class="NSColor" key="IBUITextColor" id="128895179">
|
<object class="NSColor" key="IBUITextColor" id="128895179">
|
||||||
<int key="NSColorSpace">1</int>
|
<int key="NSColorSpace">1</int>
|
||||||
<bytes key="NSRGB">MCAwIDAAA</bytes>
|
<bytes key="NSRGB">MCAwIDAAA</bytes>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ function myAdd(x, y: longint): longint; cdecl; external;
|
||||||
|
|
||||||
procedure TForm1.FormCreate(Sender: TObject);
|
procedure TForm1.FormCreate(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
// we initialize the Nimrod data structures here:
|
// we initialize the Nim data structures here:
|
||||||
NimMain();
|
NimMain();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Nimrod configuration file.
|
# Nim configuration file.
|
||||||
# The file is used only to add the path of the backend to the compiler options.
|
# The file is used only to add the path of the backend to the compiler options.
|
||||||
|
|
||||||
path="../nim_backend"
|
path="../nim_backend"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ The cross platform calculator illustrates how to use Nim to create a backend
|
||||||
called by different native user interfaces.
|
called by different native user interfaces.
|
||||||
|
|
||||||
Since the purpose of the example is to show how the cross platform code
|
Since the purpose of the example is to show how the cross platform code
|
||||||
interacts with Nimrod the actual backend code is just a simple addition proc.
|
interacts with Nim the actual backend code is just a simple addition proc.
|
||||||
By keeping your program logic in Nim you can easily reuse it in different
|
By keeping your program logic in Nim you can easily reuse it in different
|
||||||
platforms.
|
platforms.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
|
|
||||||
import db_sqlite, parseutils, strutils, times
|
import db_sqlite, parseutils, strutils, times
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TTodo* = object
|
Todo* = object
|
||||||
## A todo object holding the information serialized to the database.
|
## A todo object holding the information serialized to the database.
|
||||||
id: int64 ## Unique identifier of the object in the
|
id: int64 ## Unique identifier of the object in the
|
||||||
## database, use the getId() accessor to read it.
|
## database, use the getId() accessor to read it.
|
||||||
|
|
@ -17,7 +16,7 @@ type
|
||||||
## outside of this module, use the
|
## outside of this module, use the
|
||||||
## getModificationDate accessor.
|
## getModificationDate accessor.
|
||||||
|
|
||||||
TPagedParams* = object
|
PagedParams* = object
|
||||||
## Contains parameters for a query, initialize default values with
|
## Contains parameters for a query, initialize default values with
|
||||||
## initDefaults().
|
## initDefaults().
|
||||||
pageSize*: int64 ## Lines per returned query page, -1 for
|
pageSize*: int64 ## Lines per returned query page, -1 for
|
||||||
|
|
@ -27,11 +26,10 @@ type
|
||||||
showUnchecked*: bool ## Get unchecked objects.
|
showUnchecked*: bool ## Get unchecked objects.
|
||||||
showChecked*: bool ## Get checked objects.
|
showChecked*: bool ## Get checked objects.
|
||||||
|
|
||||||
|
|
||||||
# - General procs
|
# - General procs
|
||||||
#
|
|
||||||
proc initDefaults*(params: var TPagedParams) =
|
proc initDefaults*(params: var PagedParams) =
|
||||||
## Sets sane defaults for a TPagedParams object.
|
## Sets sane defaults for a PagedParams object.
|
||||||
##
|
##
|
||||||
## Note that you should always provide a non zero pageSize, either a specific
|
## Note that you should always provide a non zero pageSize, either a specific
|
||||||
## positive value or negative for unbounded query results.
|
## positive value or negative for unbounded query results.
|
||||||
|
|
@ -41,7 +39,6 @@ proc initDefaults*(params: var TPagedParams) =
|
||||||
params.showUnchecked = true
|
params.showUnchecked = true
|
||||||
params.showChecked = false
|
params.showChecked = false
|
||||||
|
|
||||||
|
|
||||||
proc openDatabase*(path: string): DbConn =
|
proc openDatabase*(path: string): DbConn =
|
||||||
## Creates or opens the sqlite3 database.
|
## Creates or opens the sqlite3 database.
|
||||||
##
|
##
|
||||||
|
|
@ -56,16 +53,14 @@ proc openDatabase*(path: string): DbConn =
|
||||||
desc TEXT NOT NULL,
|
desc TEXT NOT NULL,
|
||||||
modification_date INTEGER NOT NULL,
|
modification_date INTEGER NOT NULL,
|
||||||
CONSTRAINT Todos UNIQUE (id))"""
|
CONSTRAINT Todos UNIQUE (id))"""
|
||||||
|
|
||||||
db_sqlite.exec(conn, query)
|
db_sqlite.exec(conn, query)
|
||||||
result = conn
|
result = conn
|
||||||
|
|
||||||
|
# - Procs related to Todo objects
|
||||||
|
|
||||||
# - Procs related to TTodo objects
|
|
||||||
#
|
|
||||||
proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
|
proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
|
||||||
modificationDate: Time): TTodo =
|
modificationDate: Time): Todo =
|
||||||
## Returns an initialized TTodo object created from database parameters.
|
## Returns an initialized Todo object created from database parameters.
|
||||||
##
|
##
|
||||||
## The proc assumes all values are right. Note this proc is NOT exported.
|
## The proc assumes all values are right. Note this proc is NOT exported.
|
||||||
assert(id >= 0, "Identity identifiers should not be negative")
|
assert(id >= 0, "Identity identifiers should not be negative")
|
||||||
|
|
@ -75,29 +70,25 @@ proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
|
||||||
result.isDone = isDone
|
result.isDone = isDone
|
||||||
result.modificationDate = modificationDate
|
result.modificationDate = modificationDate
|
||||||
|
|
||||||
|
proc getId*(todo: Todo): int64 =
|
||||||
proc getId*(todo: TTodo): int64 =
|
|
||||||
## Accessor returning the value of the private id property.
|
## Accessor returning the value of the private id property.
|
||||||
return todo.id
|
return todo.id
|
||||||
|
|
||||||
|
proc getModificationDate*(todo: Todo): Time =
|
||||||
proc getModificationDate*(todo: TTodo): Time =
|
## Returns the last modification date of a Todo entry.
|
||||||
## Returns the last modification date of a TTodo entry.
|
|
||||||
return todo.modificationDate
|
return todo.modificationDate
|
||||||
|
|
||||||
|
proc update*(todo: var Todo; conn: DbConn): bool =
|
||||||
proc update*(todo: var TTodo; conn: DbConn): bool =
|
|
||||||
## Checks the database for the object and refreshes its variables.
|
## Checks the database for the object and refreshes its variables.
|
||||||
##
|
##
|
||||||
## Use this method if you (or another entity) have modified the database and
|
## Use this method if you (or another entity) have modified the database and
|
||||||
## want to update the object you have with whatever the database has stored.
|
## want to update the object you have with whatever the database has stored.
|
||||||
## Returns true if the update succeeded, or false if the object was not found
|
## Returns true if the update succeeded, or false if the object was not found
|
||||||
## in the database any more, in which case you should probably get rid of the
|
## in the database any more, in which case you should probably get rid of the
|
||||||
## TTodo object.
|
## Todo object.
|
||||||
assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
|
assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
|
||||||
let query = sql"""SELECT desc, priority, is_done, modification_date
|
let query = sql"""SELECT desc, priority, is_done, modification_date
|
||||||
FROM Todos WHERE id = ?"""
|
FROM Todos WHERE id = ?"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let rows = conn.getAllRows(query, $todo.id)
|
let rows = conn.getAllRows(query, $todo.id)
|
||||||
if len(rows) < 1:
|
if len(rows) < 1:
|
||||||
|
|
@ -111,8 +102,7 @@ proc update*(todo: var TTodo; conn: DbConn): bool =
|
||||||
except:
|
except:
|
||||||
echo("Something went wrong selecting for id " & $todo.id)
|
echo("Something went wrong selecting for id " & $todo.id)
|
||||||
|
|
||||||
|
proc save*(todo: var Todo; conn: DbConn): bool =
|
||||||
proc save*(todo: var TTodo; conn: DbConn): bool =
|
|
||||||
## Saves the current state of text, priority and isDone to the database.
|
## Saves the current state of text, priority and isDone to the database.
|
||||||
##
|
##
|
||||||
## Returns true if the database object was updated (in which case the
|
## Returns true if the database object was updated (in which case the
|
||||||
|
|
@ -127,15 +117,13 @@ proc save*(todo: var TTodo; conn: DbConn): bool =
|
||||||
WHERE id = ?"""
|
WHERE id = ?"""
|
||||||
rowsUpdated = conn.execAffectedRows(query, $todo.text,
|
rowsUpdated = conn.execAffectedRows(query, $todo.text,
|
||||||
$todo.priority, $todo.isDone, $int(currentDate), $todo.id)
|
$todo.priority, $todo.isDone, $int(currentDate), $todo.id)
|
||||||
|
|
||||||
if 1 == rowsUpdated:
|
if 1 == rowsUpdated:
|
||||||
todo.modificationDate = currentDate
|
todo.modificationDate = currentDate
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
|
|
||||||
# - Procs dealing directly with the database
|
# - Procs dealing directly with the database
|
||||||
#
|
|
||||||
proc addTodo*(conn: DbConn; priority: int; text: string): TTodo =
|
proc addTodo*(conn: DbConn; priority: int; text: string): Todo =
|
||||||
## Inserts a new todo into the database.
|
## Inserts a new todo into the database.
|
||||||
##
|
##
|
||||||
## Returns the generated todo object. If there is an error EDb will be raised.
|
## Returns the generated todo object. If there is an error EDb will be raised.
|
||||||
|
|
@ -145,10 +133,8 @@ proc addTodo*(conn: DbConn; priority: int; text: string): TTodo =
|
||||||
(priority, is_done, desc, modification_date)
|
(priority, is_done, desc, modification_date)
|
||||||
VALUES (?, 'false', ?, ?)"""
|
VALUES (?, 'false', ?, ?)"""
|
||||||
todoId = conn.insertId(query, priority, text, $int(currentDate))
|
todoId = conn.insertId(query, priority, text, $int(currentDate))
|
||||||
|
|
||||||
result = initFromDB(todoId, text, priority, false, currentDate)
|
result = initFromDB(todoId, text, priority, false, currentDate)
|
||||||
|
|
||||||
|
|
||||||
proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
|
proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
|
||||||
## Deletes the specified todo identifier.
|
## Deletes the specified todo identifier.
|
||||||
##
|
##
|
||||||
|
|
@ -156,7 +142,6 @@ proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
|
||||||
let query = sql"""DELETE FROM Todos WHERE id = ?"""
|
let query = sql"""DELETE FROM Todos WHERE id = ?"""
|
||||||
result = conn.execAffectedRows(query, $todoId)
|
result = conn.execAffectedRows(query, $todoId)
|
||||||
|
|
||||||
|
|
||||||
proc getNumEntries*(conn: DbConn): int =
|
proc getNumEntries*(conn: DbConn): int =
|
||||||
## Returns the number of entries in the Todos table.
|
## Returns the number of entries in the Todos table.
|
||||||
##
|
##
|
||||||
|
|
@ -170,38 +155,30 @@ proc getNumEntries*(conn: DbConn): int =
|
||||||
echo("Something went wrong retrieving number of Todos entries")
|
echo("Something went wrong retrieving number of Todos entries")
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
|
proc getPagedTodos*(conn: DbConn; params: PagedParams; page = 0'i64): seq[Todo] =
|
||||||
proc getPagedTodos*(conn: DbConn; params: TPagedParams;
|
|
||||||
page = 0'i64): seq[TTodo] =
|
|
||||||
## Returns the todo entries for a specific page.
|
## Returns the todo entries for a specific page.
|
||||||
##
|
##
|
||||||
## Pages are calculated based on the params.pageSize parameter, which can be
|
## Pages are calculated based on the params.pageSize parameter, which can be
|
||||||
## set to a negative value to specify no limit at all. The query will be
|
## set to a negative value to specify no limit at all. The query will be
|
||||||
## affected by the TPagedParams, which should have sane values (call
|
## affected by the PagedParams, which should have sane values (call
|
||||||
## initDefaults).
|
## initDefaults).
|
||||||
assert(page >= 0, "You should request a page zero or bigger than zero")
|
assert(page >= 0, "You should request a page zero or bigger than zero")
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
||||||
# Well, if you don't want to see anything, there's no point in asking the db.
|
# Well, if you don't want to see anything, there's no point in asking the db.
|
||||||
if not params.showUnchecked and not params.showChecked: return
|
if not params.showUnchecked and not params.showChecked: return
|
||||||
|
|
||||||
let
|
let
|
||||||
order_by = [
|
order_by = [
|
||||||
if params.priorityAscending: "ASC" else: "DESC",
|
if params.priorityAscending: "ASC" else: "DESC",
|
||||||
if params.dateAscending: "ASC" else: "DESC"]
|
if params.dateAscending: "ASC" else: "DESC"]
|
||||||
|
|
||||||
query = sql("""SELECT id, desc, priority, is_done, modification_date
|
query = sql("""SELECT id, desc, priority, is_done, modification_date
|
||||||
FROM Todos
|
FROM Todos
|
||||||
WHERE is_done = ? OR is_done = ?
|
WHERE is_done = ? OR is_done = ?
|
||||||
ORDER BY priority $1, modification_date $2, id DESC
|
ORDER BY priority $1, modification_date $2, id DESC
|
||||||
LIMIT ? * ?,?""" % order_by)
|
LIMIT ? * ?,?""" % order_by)
|
||||||
|
|
||||||
args = @[$params.showChecked, $(not params.showUnchecked),
|
args = @[$params.showChecked, $(not params.showUnchecked),
|
||||||
$params.pageSize, $page, $params.pageSize]
|
$params.pageSize, $page, $params.pageSize]
|
||||||
|
|
||||||
#echo("Query " & string(query))
|
#echo("Query " & string(query))
|
||||||
#echo("args: " & args.join(", "))
|
#echo("args: " & args.join(", "))
|
||||||
|
|
||||||
var newId: BiggestInt
|
var newId: BiggestInt
|
||||||
for row in conn.fastRows(query, args):
|
for row in conn.fastRows(query, args):
|
||||||
let numChars = row[0].parseBiggestInt(newId)
|
let numChars = row[0].parseBiggestInt(newId)
|
||||||
|
|
@ -209,10 +186,9 @@ proc getPagedTodos*(conn: DbConn; params: TPagedParams;
|
||||||
result.add(initFromDB(int64(newId), row[1], row[2].parseInt,
|
result.add(initFromDB(int64(newId), row[1], row[2].parseInt,
|
||||||
row[3].parseBool, Time(row[4].parseInt)))
|
row[3].parseBool, Time(row[4].parseInt)))
|
||||||
|
|
||||||
|
proc getTodo*(conn: DbConn; todoId: int64): ref Todo =
|
||||||
proc getTodo*(conn: DbConn; todoId: int64): ref TTodo =
|
## Returns a reference to a Todo or nil if the todo could not be found.
|
||||||
## Returns a reference to a TTodo or nil if the todo could not be found.
|
var tempTodo: Todo
|
||||||
var tempTodo: TTodo
|
|
||||||
tempTodo.id = todoId
|
tempTodo.id = todoId
|
||||||
if tempTodo.update(conn):
|
if tempTodo.update(conn):
|
||||||
new(result)
|
new(result)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
import backend, db_sqlite, strutils, times
|
import backend, db_sqlite, strutils, times
|
||||||
|
|
||||||
|
proc showPagedResults(conn: DbConn; params: PagedParams) =
|
||||||
proc showPagedResults(conn: DbConn; params: TPagedParams) =
|
|
||||||
## Shows the contents of the database in pages of specified size.
|
## Shows the contents of the database in pages of specified size.
|
||||||
##
|
##
|
||||||
## Hmm... I guess this is more of a debug proc which should be moved outside,
|
## Hmm... I guess this is more of a debug proc which should be moved outside,
|
||||||
|
|
@ -11,7 +10,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) =
|
||||||
var
|
var
|
||||||
page = 0'i64
|
page = 0'i64
|
||||||
rows = conn.getPagedTodos(params)
|
rows = conn.getPagedTodos(params)
|
||||||
|
|
||||||
while rows.len > 0:
|
while rows.len > 0:
|
||||||
echo("page " & $page)
|
echo("page " & $page)
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
|
@ -25,7 +23,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) =
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
proc dumTest() =
|
proc dumTest() =
|
||||||
let conn = openDatabase("todo.sqlite3")
|
let conn = openDatabase("todo.sqlite3")
|
||||||
try:
|
try:
|
||||||
|
|
@ -35,10 +32,8 @@ proc dumTest() =
|
||||||
# Fill some dummy rows if there are not many entries yet.
|
# Fill some dummy rows if there are not many entries yet.
|
||||||
discard conn.addTodo(3, "Filler1")
|
discard conn.addTodo(3, "Filler1")
|
||||||
discard conn.addTodo(4, "Filler2")
|
discard conn.addTodo(4, "Filler2")
|
||||||
|
|
||||||
var todo = conn.addTodo(2, "Testing")
|
var todo = conn.addTodo(2, "Testing")
|
||||||
echo("New todo added with id " & $todo.getId)
|
echo("New todo added with id " & $todo.getId)
|
||||||
|
|
||||||
# Try changing it and updating the database.
|
# Try changing it and updating the database.
|
||||||
var clonedTodo = conn.getTodo(todo.getId)[]
|
var clonedTodo = conn.getTodo(todo.getId)[]
|
||||||
assert(clonedTodo.text == todo.text, "Should be equal")
|
assert(clonedTodo.text == todo.text, "Should be equal")
|
||||||
|
|
@ -49,13 +44,11 @@ proc dumTest() =
|
||||||
echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone])
|
echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone])
|
||||||
else:
|
else:
|
||||||
assert(false, "Uh oh, I wasn't expecting that!")
|
assert(false, "Uh oh, I wasn't expecting that!")
|
||||||
|
|
||||||
# Verify our cloned copy is different but can be updated.
|
# Verify our cloned copy is different but can be updated.
|
||||||
assert(clonedTodo.text != todo.text, "Should be different")
|
assert(clonedTodo.text != todo.text, "Should be different")
|
||||||
discard clonedTodo.update(conn)
|
discard clonedTodo.update(conn)
|
||||||
assert(clonedTodo.text == todo.text, "Should be equal")
|
assert(clonedTodo.text == todo.text, "Should be equal")
|
||||||
|
var params: PagedParams
|
||||||
var params: TPagedParams
|
|
||||||
params.initDefaults
|
params.initDefaults
|
||||||
conn.showPagedResults(params)
|
conn.showPagedResults(params)
|
||||||
conn.deleteTodo(todo.getId)
|
conn.deleteTodo(todo.getId)
|
||||||
|
|
@ -66,7 +59,6 @@ proc dumTest() =
|
||||||
echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone])
|
echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone])
|
||||||
else:
|
else:
|
||||||
echo("Can't update object $1 from db!" % $todo.getId)
|
echo("Can't update object $1 from db!" % $todo.getId)
|
||||||
|
|
||||||
# Try to list content in a different way.
|
# Try to list content in a different way.
|
||||||
params.pageSize = 5
|
params.pageSize = 5
|
||||||
params.priorityAscending = true
|
params.priorityAscending = true
|
||||||
|
|
@ -77,7 +69,6 @@ proc dumTest() =
|
||||||
conn.close
|
conn.close
|
||||||
echo("Database closed")
|
echo("Database closed")
|
||||||
|
|
||||||
|
|
||||||
# Code that will be run only on the commandline.
|
# Code that will be run only on the commandline.
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
dumTest()
|
dumTest()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Nimrod configuration file.
|
# Nim configuration file.
|
||||||
# The file is used only to add the path of the backend to the compiler options.
|
# The file is used only to add the path of the backend to the compiler options.
|
||||||
|
|
||||||
path="../nim_backend"
|
path="../nim_backend"
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ Commands:
|
||||||
-h, --help shows this help
|
-h, --help shows this help
|
||||||
|
|
||||||
List options (optional):
|
List options (optional):
|
||||||
-p=+|- Sorts list by ascending|desdencing priority. Default:desdencing.
|
-p=+|- Sorts list by ascending|descending priority. Default:descending.
|
||||||
-m=+|- Sorts list by ascending|desdencing date. Default:desdencing.
|
-m=+|- Sorts list by ascending|descending date. Default:descending.
|
||||||
-t Show checked entries. By default they are not shown.
|
-t Show checked entries. By default they are not shown.
|
||||||
-z Hide unchecked entries. By default they are shown.
|
-z Hide unchecked entries. By default they are shown.
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ Examples:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
TCommand = enum # The possible types of commands
|
Command = enum # The possible types of commands
|
||||||
cmdAdd # The user wants to add a new todo entry.
|
cmdAdd # The user wants to add a new todo entry.
|
||||||
cmdCheck # User wants to check a todo entry.
|
cmdCheck # User wants to check a todo entry.
|
||||||
cmdUncheck # User wants to uncheck a todo entry.
|
cmdUncheck # User wants to uncheck a todo entry.
|
||||||
|
|
@ -42,30 +42,27 @@ type
|
||||||
cmdGenerate # Add random rows to the database, for testing.
|
cmdGenerate # Add random rows to the database, for testing.
|
||||||
cmdList # User wants to list contents.
|
cmdList # User wants to list contents.
|
||||||
|
|
||||||
TParamConfig = object
|
ParamConfig = object
|
||||||
# Structure containing the parsed options from the commandline.
|
# Structure containing the parsed options from the commandline.
|
||||||
command: TCommand # Store the type of operation
|
command: Command # Store the type of operation
|
||||||
addPriority: int # Only valid with cmdAdd, stores priority.
|
addPriority: int # Only valid with cmdAdd, stores priority.
|
||||||
addText: seq[string] # Only valid with cmdAdd, stores todo text.
|
addText: seq[string] # Only valid with cmdAdd, stores todo text.
|
||||||
todoId: int64 # The todo id for operations like check or delete.
|
todoId: int64 # The todo id for operations like check or delete.
|
||||||
listParams: TPagedParams # Uses the backend structure directly for params.
|
listParams: PagedParams # Uses the backend structure directly for params.
|
||||||
|
|
||||||
|
proc initDefaults(params: var ParamConfig) =
|
||||||
proc initDefaults(params: var TParamConfig) =
|
|
||||||
## Initialises defaults value in the structure.
|
## Initialises defaults value in the structure.
|
||||||
##
|
##
|
||||||
## Most importantly we want to have an empty list for addText.
|
## Most importantly we want to have an empty list for addText.
|
||||||
params.listParams.initDefaults
|
params.listParams.initDefaults
|
||||||
params.addText = @[]
|
params.addText = @[]
|
||||||
|
|
||||||
|
|
||||||
proc abort(message: string, value: int) =
|
proc abort(message: string, value: int) =
|
||||||
# Simple wrapper to abort also displaying the help to the user.
|
# Simple wrapper to abort also displaying the help to the user.
|
||||||
stdout.write(USAGE)
|
stdout.write(USAGE)
|
||||||
quit(message, value)
|
quit(message, value)
|
||||||
|
|
||||||
|
template parseTodoIdAndSetCommand(newCommand: Command): untyped =
|
||||||
template parseTodoIdAndSetCommand(newCommand: TCommand): stmt =
|
|
||||||
## Helper to parse a big todo identifier into todoId and set command.
|
## Helper to parse a big todo identifier into todoId and set command.
|
||||||
try:
|
try:
|
||||||
let numChars = val.parseBiggestInt(newId)
|
let numChars = val.parseBiggestInt(newId)
|
||||||
|
|
@ -75,8 +72,7 @@ template parseTodoIdAndSetCommand(newCommand: TCommand): stmt =
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
raise newException(ValueError, "Value $1 too big" % val)
|
raise newException(ValueError, "Value $1 too big" % val)
|
||||||
|
|
||||||
|
template verifySingleCommand(actions: typed): typed =
|
||||||
template verifySingleCommand(actions: stmt): stmt =
|
|
||||||
## Helper to make sure only one command has been specified so far.
|
## Helper to make sure only one command has been specified so far.
|
||||||
if specifiedCommand:
|
if specifiedCommand:
|
||||||
abort("Only one command can be specified at a time! (extra:$1)" % [key], 2)
|
abort("Only one command can be specified at a time! (extra:$1)" % [key], 2)
|
||||||
|
|
@ -84,7 +80,6 @@ template verifySingleCommand(actions: stmt): stmt =
|
||||||
actions
|
actions
|
||||||
specifiedCommand = true
|
specifiedCommand = true
|
||||||
|
|
||||||
|
|
||||||
proc parsePlusMinus(val: string, debugText: string): bool =
|
proc parsePlusMinus(val: string, debugText: string): bool =
|
||||||
## Helper to process a plus or minus character from the commandline.
|
## Helper to process a plus or minus character from the commandline.
|
||||||
##
|
##
|
||||||
|
|
@ -100,11 +95,10 @@ proc parsePlusMinus(val: string, debugText: string): bool =
|
||||||
else:
|
else:
|
||||||
abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4)
|
abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4)
|
||||||
|
|
||||||
|
proc parseCmdLine(): ParamConfig =
|
||||||
proc parseCmdLine(): TParamConfig =
|
|
||||||
## Parses the commandline.
|
## Parses the commandline.
|
||||||
##
|
##
|
||||||
## Returns a TParamConfig structure filled with the proper values or directly
|
## Returns a ParamConfig structure filled with the proper values or directly
|
||||||
## calls quit() with the appropriate error message.
|
## calls quit() with the appropriate error message.
|
||||||
var
|
var
|
||||||
specifiedCommand = false
|
specifiedCommand = false
|
||||||
|
|
@ -112,15 +106,12 @@ proc parseCmdLine(): TParamConfig =
|
||||||
p = initOptParser()
|
p = initOptParser()
|
||||||
key, val: TaintedString
|
key, val: TaintedString
|
||||||
newId: BiggestInt
|
newId: BiggestInt
|
||||||
|
|
||||||
result.initDefaults
|
result.initDefaults
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while true:
|
while true:
|
||||||
next(p)
|
next(p)
|
||||||
key = p.key
|
key = p.key
|
||||||
val = p.val
|
val = p.val
|
||||||
|
|
||||||
case p.kind
|
case p.kind
|
||||||
of cmdArgument:
|
of cmdArgument:
|
||||||
if specifiedCommand and cmdAdd == result.command:
|
if specifiedCommand and cmdAdd == result.command:
|
||||||
|
|
@ -180,17 +171,13 @@ proc parseCmdLine(): TParamConfig =
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7)
|
abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7)
|
||||||
|
|
||||||
if not specifiedCommand:
|
if not specifiedCommand:
|
||||||
abort("Didn't specify any command.", 8)
|
abort("Didn't specify any command.", 8)
|
||||||
|
|
||||||
if cmdAdd == result.command and result.addText.len < 1:
|
if cmdAdd == result.command and result.addText.len < 1:
|
||||||
abort("Used the add command, but provided no text/description.", 9)
|
abort("Used the add command, but provided no text/description.", 9)
|
||||||
|
|
||||||
if usesListParams and cmdList != result.command:
|
if usesListParams and cmdList != result.command:
|
||||||
abort("Used list options, but didn't specify the list command.", 10)
|
abort("Used list options, but didn't specify the list command.", 10)
|
||||||
|
|
||||||
|
|
||||||
proc generateDatabaseRows(conn: DbConn) =
|
proc generateDatabaseRows(conn: DbConn) =
|
||||||
## Adds some rows to the database ignoring errors.
|
## Adds some rows to the database ignoring errors.
|
||||||
discard conn.addTodo(1, "Watch another random youtube video")
|
discard conn.addTodo(1, "Watch another random youtube video")
|
||||||
|
|
@ -208,19 +195,16 @@ proc generateDatabaseRows(conn: DbConn) =
|
||||||
discard conn.addTodo(6, "Learn a functional programming language")
|
discard conn.addTodo(6, "Learn a functional programming language")
|
||||||
echo("Generated some entries, they were added to your database.")
|
echo("Generated some entries, they were added to your database.")
|
||||||
|
|
||||||
|
proc listDatabaseContents(conn: DbConn; listParams: PagedParams) =
|
||||||
proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) =
|
|
||||||
## Dumps the database contents formatted to the standard output.
|
## Dumps the database contents formatted to the standard output.
|
||||||
##
|
##
|
||||||
## Pass the list/filter parameters parsed from the commandline.
|
## Pass the list/filter parameters parsed from the commandline.
|
||||||
var params = listParams
|
var params = listParams
|
||||||
params.pageSize = -1
|
params.pageSize = -1
|
||||||
|
|
||||||
let todos = conn.getPagedTodos(params)
|
let todos = conn.getPagedTodos(params)
|
||||||
if todos.len < 1:
|
if todos.len < 1:
|
||||||
echo("Database empty")
|
echo("Database empty")
|
||||||
return
|
return
|
||||||
|
|
||||||
echo("Todo id, is done, priority, last modification date, text:")
|
echo("Todo id, is done, priority, last modification date, text:")
|
||||||
# First detect how long should be our columns for formatting.
|
# First detect how long should be our columns for formatting.
|
||||||
var cols: array[0..2, int]
|
var cols: array[0..2, int]
|
||||||
|
|
@ -228,7 +212,6 @@ proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) =
|
||||||
cols[0] = max(cols[0], ($todo.getId).len)
|
cols[0] = max(cols[0], ($todo.getId).len)
|
||||||
cols[1] = max(cols[1], ($todo.priority).len)
|
cols[1] = max(cols[1], ($todo.priority).len)
|
||||||
cols[2] = max(cols[2], ($todo.getModificationDate).len)
|
cols[2] = max(cols[2], ($todo.getModificationDate).len)
|
||||||
|
|
||||||
# Now dump all the rows using the calculated alignment sizes.
|
# Now dump all the rows using the calculated alignment sizes.
|
||||||
for todo in todos:
|
for todo in todos:
|
||||||
echo("$1 $2 $3, $4, $5" % [
|
echo("$1 $2 $3, $4, $5" % [
|
||||||
|
|
@ -238,7 +221,6 @@ proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) =
|
||||||
($todo.getModificationDate).align(cols[2]),
|
($todo.getModificationDate).align(cols[2]),
|
||||||
todo.text])
|
todo.text])
|
||||||
|
|
||||||
|
|
||||||
proc deleteOneTodo(conn: DbConn; todoId: int64) =
|
proc deleteOneTodo(conn: DbConn; todoId: int64) =
|
||||||
## Deletes a single todo entry from the database.
|
## Deletes a single todo entry from the database.
|
||||||
let numDeleted = conn.deleteTodo(todoId)
|
let numDeleted = conn.deleteTodo(todoId)
|
||||||
|
|
@ -247,7 +229,6 @@ proc deleteOneTodo(conn: DbConn; todoId: int64) =
|
||||||
else:
|
else:
|
||||||
quit("Couldn't delete todo id " & $todoId, 11)
|
quit("Couldn't delete todo id " & $todoId, 11)
|
||||||
|
|
||||||
|
|
||||||
proc deleteAllTodos(conn: DbConn) =
|
proc deleteAllTodos(conn: DbConn) =
|
||||||
## Deletes all the contents from the database.
|
## Deletes all the contents from the database.
|
||||||
##
|
##
|
||||||
|
|
@ -256,43 +237,35 @@ proc deleteAllTodos(conn: DbConn) =
|
||||||
## ourselfves to the API exported by backend.
|
## ourselfves to the API exported by backend.
|
||||||
var
|
var
|
||||||
counter: int64
|
counter: int64
|
||||||
params: TPagedParams
|
params: PagedParams
|
||||||
|
|
||||||
params.initDefaults
|
params.initDefaults
|
||||||
params.pageSize = -1
|
params.pageSize = -1
|
||||||
params.showUnchecked = true
|
params.showUnchecked = true
|
||||||
params.showChecked = true
|
params.showChecked = true
|
||||||
|
|
||||||
let todos = conn.getPagedTodos(params)
|
let todos = conn.getPagedTodos(params)
|
||||||
for todo in todos:
|
for todo in todos:
|
||||||
if conn.deleteTodo(todo.getId) > 0:
|
if conn.deleteTodo(todo.getId) > 0:
|
||||||
counter += 1
|
counter += 1
|
||||||
else:
|
else:
|
||||||
quit("Couldn't delete todo id " & $todo.getId, 12)
|
quit("Couldn't delete todo id " & $todo.getId, 12)
|
||||||
|
|
||||||
echo("Deleted $1 todo entries from database." % $counter)
|
echo("Deleted $1 todo entries from database." % $counter)
|
||||||
|
|
||||||
|
|
||||||
proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) =
|
proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) =
|
||||||
## Changes the check state of a todo entry to the specified value.
|
## Changes the check state of a todo entry to the specified value.
|
||||||
let
|
let
|
||||||
newState = if value: "checked" else: "unchecked"
|
newState = if value: "checked" else: "unchecked"
|
||||||
todo = conn.getTodo(todoId)
|
todo = conn.getTodo(todoId)
|
||||||
|
|
||||||
if todo == nil:
|
if todo == nil:
|
||||||
quit("Can't modify todo id $1, its not in the database." % $todoId, 13)
|
quit("Can't modify todo id $1, its not in the database." % $todoId, 13)
|
||||||
|
|
||||||
if todo[].isDone == value:
|
if todo[].isDone == value:
|
||||||
echo("Todo id $1 was already set to $2." % [$todoId, newState])
|
echo("Todo id $1 was already set to $2." % [$todoId, newState])
|
||||||
return
|
return
|
||||||
|
|
||||||
todo[].isDone = value
|
todo[].isDone = value
|
||||||
if todo[].save(conn):
|
if todo[].save(conn):
|
||||||
echo("Todo id $1 set to $2." % [$todoId, newState])
|
echo("Todo id $1 set to $2." % [$todoId, newState])
|
||||||
else:
|
else:
|
||||||
quit("Error updating todo id $1 to $2." % [$todoId, newState])
|
quit("Error updating todo id $1 to $2." % [$todoId, newState])
|
||||||
|
|
||||||
|
|
||||||
proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) =
|
proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) =
|
||||||
## Adds to the database a todo with the specified priority.
|
## Adds to the database a todo with the specified priority.
|
||||||
##
|
##
|
||||||
|
|
@ -302,17 +275,14 @@ proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) =
|
||||||
echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [
|
echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [
|
||||||
$todo.getId, $todo.priority, todo.text])
|
$todo.getId, $todo.priority, todo.text])
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
## Main entry point.
|
## Main entry point.
|
||||||
let
|
let
|
||||||
opt = parseCmdLine()
|
opt = parseCmdLine()
|
||||||
dbPath = getConfigDir() / "nimtodo.sqlite3"
|
dbPath = getConfigDir() / "nimtodo.sqlite3"
|
||||||
|
|
||||||
if not dbPath.existsFile:
|
if not dbPath.existsFile:
|
||||||
createDir(getConfigDir())
|
createDir(getConfigDir())
|
||||||
echo("No database found at $1, it will be created for you." % dbPath)
|
echo("No database found at $1, it will be created for you." % dbPath)
|
||||||
|
|
||||||
let conn = openDatabase(dbPath)
|
let conn = openDatabase(dbPath)
|
||||||
try:
|
try:
|
||||||
case opt.command
|
case opt.command
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,6 @@ generation switch can be used to fill the database with some basic todo entries
|
||||||
you can play with.
|
you can play with.
|
||||||
|
|
||||||
Compilation is fairly easy despite having the source split in different
|
Compilation is fairly easy despite having the source split in different
|
||||||
directories. Thanks to the Nim.cfg file, which adds the ../Nim_backend
|
directories. Thanks to the nim.cfg file, which adds the ../Nim_backend
|
||||||
directory as a search path, you can compile and run the example just fine from
|
directory as a search path, you can compile and run the example just fine from
|
||||||
the command line with 'nim c -r nimtodo.nim'.
|
the command line with 'nim c -r nimtodo.nim'.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
The cross platform todo illustrates how to use Nim to create a backend
|
This cross platform todo illustrates how to use Nim to create a backend
|
||||||
called by different native user interfaces.
|
called by different native user interfaces.
|
||||||
|
|
||||||
This example builds on the knowledge learned from the cross_calculator example.
|
This example builds on the knowledge learned from the cross_calculator example.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import re
|
||||||
|
|
||||||
for x in lines("myfile.txt"):
|
for x in lines("myfile.txt"):
|
||||||
if x =~ re"(\w+)=(.*)":
|
if x =~ re"(\w+)=(.*)":
|
||||||
echo "Key: ", matches[0],
|
echo "Key: ", matches[0], " Value: ", matches[1]
|
||||||
" Value: ", matches[1]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
In this directory you will find several examples for how to use the Nimrod
|
In this directory you will find several examples for how to use the Nim
|
||||||
library.
|
library.
|
||||||
|
|
||||||
Copyright (c) 2004-2012 Andreas Rumpf.
|
Copyright (c) 2004-2012 Andreas Rumpf.
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
template html(name: expr, matter: stmt) {.immediate.} =
|
template html(name, matter: untyped) =
|
||||||
proc name(): string =
|
proc name(): string =
|
||||||
result = "<html>"
|
result = "<html>"
|
||||||
matter
|
matter
|
||||||
result.add("</html>")
|
result.add("</html>")
|
||||||
|
|
||||||
template nestedTag(tag: expr) {.immediate.} =
|
template nestedTag(tag: untyped) =
|
||||||
template tag(matter: stmt) {.immediate.} =
|
template tag(matter: typed) =
|
||||||
result.add("<" & astToStr(tag) & ">")
|
result.add("<" & astToStr(tag) & ">")
|
||||||
matter
|
matter
|
||||||
result.add("</" & astToStr(tag) & ">")
|
result.add("</" & astToStr(tag) & ">")
|
||||||
|
|
||||||
template simpleTag(tag: expr) {.immediate.} =
|
template simpleTag(tag: untyped) =
|
||||||
template tag(matter: expr) {.immediate.} =
|
template tag(matter: untyped) =
|
||||||
result.add("<$1>$2</$1>" % [astToStr(tag), matter])
|
result.add("<$1>$2</$1>" % [astToStr(tag), matter])
|
||||||
|
|
||||||
nestedTag body
|
nestedTag body
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import macros
|
||||||
proc invalidFormatString() =
|
proc invalidFormatString() =
|
||||||
echo "invalidFormatString"
|
echo "invalidFormatString"
|
||||||
|
|
||||||
template formatImpl(handleChar: expr) =
|
template formatImpl(handleChar: untyped) =
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < f.len:
|
while i < f.len:
|
||||||
if f[i] == '$':
|
if f[i] == '$':
|
||||||
|
|
@ -24,11 +24,11 @@ template formatImpl(handleChar: expr) =
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
proc `%`*(f: string, a: openArray[string]): string =
|
proc `%`*(f: string, a: openArray[string]): string =
|
||||||
template identity(x: expr): expr = x
|
template identity(x: untyped): untyped = x
|
||||||
result = ""
|
result = ""
|
||||||
formatImpl(identity)
|
formatImpl(identity)
|
||||||
|
|
||||||
macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): expr =
|
macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped =
|
||||||
result = newNimNode(nnkBracket)
|
result = newNimNode(nnkBracket)
|
||||||
#newCall("&")
|
#newCall("&")
|
||||||
let f = f.strVal
|
let f = f.strVal
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
macro check(ex: expr): stmt =
|
macro check(ex: untyped): typed =
|
||||||
var info = ex.lineinfo
|
var info = ex.lineinfo
|
||||||
var expString = ex.toStrLit
|
var expString = ex.toStrLit
|
||||||
result = quote do:
|
result = quote do:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
template htmlTag(tag: expr) {.immediate.} =
|
template htmlTag(tag: untyped) =
|
||||||
proc tag(): string = "<" & astToStr(tag) & ">"
|
proc tag(): string = "<" & astToStr(tag) & ">"
|
||||||
|
|
||||||
htmlTag(br)
|
htmlTag(br)
|
||||||
htmlTag(html)
|
htmlTag(html)
|
||||||
|
|
||||||
echo br()
|
echo br()
|
||||||
|
echo html()
|
||||||
4
koch.nim
4
koch.nim
|
|
@ -473,7 +473,7 @@ proc temp(args: string) =
|
||||||
# commit.
|
# commit.
|
||||||
let (bootArgs, programArgs) = splitArgs(args)
|
let (bootArgs, programArgs) = splitArgs(args)
|
||||||
let nimexec = findNim()
|
let nimexec = findNim()
|
||||||
exec(nimexec & " c " & bootArgs & " compiler" / "nim", 125)
|
exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125)
|
||||||
copyExe(output, finalDest)
|
copyExe(output, finalDest)
|
||||||
if programArgs.len > 0: exec(finalDest & " " & programArgs)
|
if programArgs.len > 0: exec(finalDest & " " & programArgs)
|
||||||
|
|
||||||
|
|
@ -482,7 +482,7 @@ proc xtemp(cmd: string) =
|
||||||
copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe)
|
copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe)
|
||||||
try:
|
try:
|
||||||
withDir(d):
|
withDir(d):
|
||||||
temp"-d:debug"
|
temp""
|
||||||
copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
|
copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
|
||||||
exec(cmd)
|
exec(cmd)
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,9 @@ type
|
||||||
|
|
||||||
type
|
type
|
||||||
NimIdent* = object of RootObj
|
NimIdent* = object of RootObj
|
||||||
## represents a Nim identifier in the AST
|
## represents a Nim identifier in the AST. **Note**: This is only
|
||||||
|
## rarely useful, for identifier construction from a string
|
||||||
|
## use ``ident"abc"``.
|
||||||
|
|
||||||
NimSymObj = object # hidden
|
NimSymObj = object # hidden
|
||||||
NimSym* = ref NimSymObj
|
NimSym* = ref NimSymObj
|
||||||
|
|
@ -129,7 +131,11 @@ const
|
||||||
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||||
nnkCallStrLit}
|
nnkCallStrLit}
|
||||||
|
|
||||||
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
|
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
|
||||||
|
## constructs an identifier from the string `s`
|
||||||
|
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
|
||||||
|
|
||||||
|
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
|
||||||
## constructs an identifier from the string `s`
|
## constructs an identifier from the string `s`
|
||||||
|
|
||||||
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
|
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
|
||||||
|
|
@ -237,7 +243,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
|
||||||
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
|
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
|
||||||
|
|
||||||
proc newNimNode*(kind: NimNodeKind,
|
proc newNimNode*(kind: NimNodeKind,
|
||||||
lineInfoFrom: NimNode=nil): NimNode
|
lineInfoFrom: NimNode = nil): NimNode
|
||||||
{.magic: "NNewNimNode", noSideEffect.}
|
{.magic: "NNewNimNode", noSideEffect.}
|
||||||
## Creates a new AST node of the specified kind.
|
## Creates a new AST node of the specified kind.
|
||||||
##
|
##
|
||||||
|
|
@ -290,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
|
||||||
proc newIdentNode*(i: string): NimNode {.compileTime.} =
|
proc newIdentNode*(i: string): NimNode {.compileTime.} =
|
||||||
## creates an identifier node from `i`
|
## creates an identifier node from `i`
|
||||||
result = newNimNode(nnkIdent)
|
result = newNimNode(nnkIdent)
|
||||||
result.ident = !i
|
result.ident = toNimIdent i
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -400,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## macro check(ex: expr): stmt =
|
## macro check(ex: untyped): typed =
|
||||||
## # this is a simplified version of the check macro from the
|
## # this is a simplified version of the check macro from the
|
||||||
## # unittest module.
|
## # unittest module.
|
||||||
##
|
##
|
||||||
|
|
@ -477,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
|
||||||
result = newNimNode(nnkCharLit)
|
result = newNimNode(nnkCharLit)
|
||||||
result.intVal = ord(c)
|
result.intVal = ord(c)
|
||||||
|
|
||||||
|
|
||||||
proc newLit*(i: int): NimNode {.compileTime.} =
|
proc newLit*(i: int): NimNode {.compileTime.} =
|
||||||
## produces a new integer literal node.
|
## produces a new integer literal node.
|
||||||
result = newNimNode(nnkIntLit)
|
result = newNimNode(nnkIntLit)
|
||||||
|
|
@ -581,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
|
||||||
proc newLit*(arg: tuple): NimNode {.compileTime.} =
|
proc newLit*(arg: tuple): NimNode {.compileTime.} =
|
||||||
result = nnkPar.newTree
|
result = nnkPar.newTree
|
||||||
for a,b in arg.fieldPairs:
|
for a,b in arg.fieldPairs:
|
||||||
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
|
result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
|
||||||
|
|
||||||
proc newLit*(s: string): NimNode {.compileTime.} =
|
proc newLit*(s: string): NimNode {.compileTime.} =
|
||||||
## produces a new string literal node.
|
## produces a new string literal node.
|
||||||
|
|
@ -615,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
|
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
|
||||||
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
|
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
|
||||||
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
|
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
|
||||||
of nnkIdent: res.add(" !\"" & $n.ident & '"')
|
of nnkIdent: res.add(" ident\"" & $n.ident & '"')
|
||||||
of nnkSym: res.add(" \"" & $n.symbol & '"')
|
of nnkSym: res.add(" \"" & $n.symbol & '"')
|
||||||
of nnkNone: assert false
|
of nnkNone: assert false
|
||||||
else:
|
else:
|
||||||
|
|
@ -640,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
||||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
||||||
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
||||||
of nnkIdent: add(result, "!\"" & $n.ident & '"')
|
of nnkIdent: add(result, "ident\"" & $n.ident & '"')
|
||||||
of nnkSym: add(result, $n.symbol)
|
of nnkSym: add(result, $n.symbol)
|
||||||
of nnkNone: assert false
|
of nnkNone: assert false
|
||||||
else:
|
else:
|
||||||
|
|
@ -664,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## nnkStmtList.newTree(
|
## nnkStmtList.newTree(
|
||||||
## nnkCommand.newTree(
|
## nnkCommand.newTree(
|
||||||
## newIdentNode(!"echo"),
|
## newIdentNode("echo"),
|
||||||
## newLit("Hello world")
|
## newLit("Hello world")
|
||||||
## )
|
## )
|
||||||
## )
|
## )
|
||||||
|
|
@ -718,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
|
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
|
||||||
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
|
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
|
||||||
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
|
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
|
||||||
of nnkIdent: res.add("!" & ($n.ident).escape())
|
of nnkIdent: res.add(($n.ident).escape())
|
||||||
of nnkSym: res.add(($n.symbol).escape())
|
of nnkSym: res.add(($n.symbol).escape())
|
||||||
of nnkNone: assert false
|
of nnkNone: assert false
|
||||||
else:
|
else:
|
||||||
|
|
@ -898,6 +903,48 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
|
||||||
for i in branches:
|
for i in branches:
|
||||||
result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
|
result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
|
||||||
|
|
||||||
|
proc newEnum*(name: NimNode, fields: openArray[NimNode],
|
||||||
|
public, pure: bool): NimNode {.compileTime.} =
|
||||||
|
|
||||||
|
## Creates a new enum. `name` must be an ident. Fields are allowed to be
|
||||||
|
## either idents or EnumFieldDef
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## newEnum(
|
||||||
|
## name = ident("Colors"),
|
||||||
|
## fields = [ident("Blue"), ident("Red")],
|
||||||
|
## public = true, pure = false)
|
||||||
|
##
|
||||||
|
## # type Colors* = Blue Red
|
||||||
|
##
|
||||||
|
|
||||||
|
expectKind name, nnkIdent
|
||||||
|
doAssert len(fields) > 0, "Enum must contain at least one field"
|
||||||
|
for field in fields:
|
||||||
|
expectKind field, {nnkIdent, nnkEnumFieldDef}
|
||||||
|
|
||||||
|
let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields)
|
||||||
|
var typeDefArgs = [name, newEmptyNode(), enumBody]
|
||||||
|
|
||||||
|
if public:
|
||||||
|
let postNode = newNimNode(nnkPostfix).add(
|
||||||
|
newIdentNode("*"), typeDefArgs[0])
|
||||||
|
|
||||||
|
typeDefArgs[0] = postNode
|
||||||
|
|
||||||
|
if pure:
|
||||||
|
let pragmaNode = newNimNode(nnkPragmaExpr).add(
|
||||||
|
typeDefArgs[0],
|
||||||
|
add(newNimNode(nnkPragma), newIdentNode("pure")))
|
||||||
|
|
||||||
|
typeDefArgs[0] = pragmaNode
|
||||||
|
|
||||||
|
let
|
||||||
|
typeDef = add(newNimNode(nnkTypeDef), typeDefArgs)
|
||||||
|
typeSect = add(newNimNode(nnkTypeSection), typeDef)
|
||||||
|
|
||||||
|
return typeSect
|
||||||
|
|
||||||
proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
|
proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
|
||||||
## Copy all children from `src` to `dest`
|
## Copy all children from `src` to `dest`
|
||||||
|
|
@ -1015,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## var res = findChild(n, it.kind == nnkPostfix and
|
## var res = findChild(n, it.kind == nnkPostfix and
|
||||||
## it.basename.ident == !"foo")
|
## it.basename.ident == toNimIdent"foo")
|
||||||
block:
|
block:
|
||||||
var res: NimNode
|
var res: NimNode
|
||||||
for it in n.children:
|
for it in n.children:
|
||||||
|
|
@ -1049,7 +1096,7 @@ proc basename*(a: NimNode): NimNode =
|
||||||
|
|
||||||
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
|
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
|
||||||
case a.kind
|
case a.kind
|
||||||
of nnkIdent: macros.`ident=`(a, !val)
|
of nnkIdent: macros.`ident=`(a, toNimIdent val)
|
||||||
of nnkPostfix, nnkPrefix: a[1] = ident(val)
|
of nnkPostfix, nnkPrefix: a[1] = ident(val)
|
||||||
else:
|
else:
|
||||||
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
|
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
|
||||||
|
|
@ -1110,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
|
||||||
## other ways like ``node.ident`` are much more error-prone, unfortunately.
|
## other ways like ``node.ident`` are much more error-prone, unfortunately.
|
||||||
case node.kind
|
case node.kind
|
||||||
of nnkIdent:
|
of nnkIdent:
|
||||||
result = node.ident == !s
|
result = node.ident == toNimIdent s
|
||||||
of nnkSym:
|
of nnkSym:
|
||||||
result = eqIdent($node.symbol, s)
|
result = eqIdent($node.symbol, s)
|
||||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ export nativesockets
|
||||||
|
|
||||||
{.warning: "rawsockets module is deprecated, use nativesockets instead".}
|
{.warning: "rawsockets module is deprecated, use nativesockets instead".}
|
||||||
|
|
||||||
template newRawSocket*(domain, sockType, protocol: cint): expr =
|
template newRawSocket*(domain, sockType, protocol: cint): untyped =
|
||||||
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
||||||
newNativeSocket(domain, sockType, protocol)
|
newNativeSocket(domain, sockType, protocol)
|
||||||
|
|
||||||
template newRawSocket*(domain: Domain = AF_INET,
|
template newRawSocket*(domain: Domain = AF_INET,
|
||||||
sockType: SockType = SOCK_STREAM,
|
sockType: SockType = SOCK_STREAM,
|
||||||
protocol: Protocol = IPPROTO_TCP): expr =
|
protocol: Protocol = IPPROTO_TCP): untyped =
|
||||||
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
||||||
newNativeSocket(domain, sockType, protocol)
|
newNativeSocket(domain, sockType, protocol)
|
||||||
|
|
|
||||||
|
|
@ -58,15 +58,18 @@ type
|
||||||
socket: AsyncSocket
|
socket: AsyncSocket
|
||||||
reuseAddr: bool
|
reuseAddr: bool
|
||||||
reusePort: bool
|
reusePort: bool
|
||||||
|
maxBody: int ## The maximum content-length that will be read for the body.
|
||||||
|
|
||||||
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
|
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
|
||||||
THttpCode: HttpCode, THttpVersion: HttpVersion].}
|
THttpCode: HttpCode, THttpVersion: HttpVersion].}
|
||||||
|
|
||||||
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer =
|
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
|
||||||
|
maxBody = 8388608): AsyncHttpServer =
|
||||||
## Creates a new ``AsyncHttpServer`` instance.
|
## Creates a new ``AsyncHttpServer`` instance.
|
||||||
new result
|
new result
|
||||||
result.reuseAddr = reuseAddr
|
result.reuseAddr = reuseAddr
|
||||||
result.reusePort = reusePort
|
result.reusePort = reusePort
|
||||||
|
result.maxBody = maxBody
|
||||||
|
|
||||||
proc addHeaders(msg: var string, headers: HttpHeaders) =
|
proc addHeaders(msg: var string, headers: HttpHeaders) =
|
||||||
for k, v in headers:
|
for k, v in headers:
|
||||||
|
|
@ -122,145 +125,161 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
|
||||||
raise newException(ValueError, "Invalid request protocol. Got: " &
|
raise newException(ValueError, "Invalid request protocol. Got: " &
|
||||||
protocol)
|
protocol)
|
||||||
result.orig = protocol
|
result.orig = protocol
|
||||||
i.inc protocol.parseInt(result.major, i)
|
i.inc protocol.parseSaturatedNatural(result.major, i)
|
||||||
i.inc # Skip .
|
i.inc # Skip .
|
||||||
i.inc protocol.parseInt(result.minor, i)
|
i.inc protocol.parseSaturatedNatural(result.minor, i)
|
||||||
|
|
||||||
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
||||||
client.send("HTTP/1.1 " & status & "\c\L\c\L")
|
client.send("HTTP/1.1 " & status & "\c\L\c\L")
|
||||||
|
|
||||||
proc processClient(client: AsyncSocket, address: string,
|
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
callback: proc (request: Request):
|
client: AsyncSocket,
|
||||||
|
address: string, lineFut: FutureVar[string],
|
||||||
|
callback: proc (request: Request):
|
||||||
Future[void] {.closure, gcsafe.}) {.async.} =
|
Future[void] {.closure, gcsafe.}) {.async.} =
|
||||||
var request: Request
|
|
||||||
request.url = initUri()
|
|
||||||
request.headers = newHttpHeaders()
|
|
||||||
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
|
|
||||||
lineFut.mget() = newStringOfCap(80)
|
|
||||||
var key, value = ""
|
|
||||||
|
|
||||||
while not client.isClosed:
|
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
|
||||||
# GET /path HTTP/1.1
|
template request(): Request =
|
||||||
# Header: val
|
req.mget()
|
||||||
# \n
|
|
||||||
request.headers.clear()
|
|
||||||
request.body = ""
|
|
||||||
request.hostname.shallowCopy(address)
|
|
||||||
assert client != nil
|
|
||||||
request.client = client
|
|
||||||
|
|
||||||
# We should skip at least one empty line before the request
|
# GET /path HTTP/1.1
|
||||||
# https://tools.ietf.org/html/rfc7230#section-3.5
|
# Header: val
|
||||||
for i in 0..1:
|
# \n
|
||||||
lineFut.mget().setLen(0)
|
request.headers.clear()
|
||||||
lineFut.clean()
|
request.body = ""
|
||||||
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
|
request.hostname.shallowCopy(address)
|
||||||
|
assert client != nil
|
||||||
|
request.client = client
|
||||||
|
|
||||||
if lineFut.mget == "":
|
# We should skip at least one empty line before the request
|
||||||
client.close()
|
# https://tools.ietf.org/html/rfc7230#section-3.5
|
||||||
return
|
for i in 0..1:
|
||||||
|
lineFut.mget().setLen(0)
|
||||||
|
lineFut.clean()
|
||||||
|
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
|
||||||
|
|
||||||
if lineFut.mget.len > maxLine:
|
if lineFut.mget == "":
|
||||||
await request.respondError(Http413)
|
client.close()
|
||||||
client.close()
|
|
||||||
return
|
|
||||||
if lineFut.mget != "\c\L":
|
|
||||||
break
|
|
||||||
|
|
||||||
# First line - GET /path HTTP/1.1
|
|
||||||
var i = 0
|
|
||||||
for linePart in lineFut.mget.split(' '):
|
|
||||||
case i
|
|
||||||
of 0:
|
|
||||||
try:
|
|
||||||
# TODO: this is likely slow.
|
|
||||||
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
|
|
||||||
except ValueError:
|
|
||||||
asyncCheck request.respondError(Http400)
|
|
||||||
continue
|
|
||||||
of 1:
|
|
||||||
try:
|
|
||||||
parseUri(linePart, request.url)
|
|
||||||
except ValueError:
|
|
||||||
asyncCheck request.respondError(Http400)
|
|
||||||
continue
|
|
||||||
of 2:
|
|
||||||
try:
|
|
||||||
request.protocol = parseProtocol(linePart)
|
|
||||||
except ValueError:
|
|
||||||
asyncCheck request.respondError(Http400)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
await request.respondError(Http400)
|
|
||||||
continue
|
|
||||||
inc i
|
|
||||||
|
|
||||||
# Headers
|
|
||||||
while true:
|
|
||||||
i = 0
|
|
||||||
lineFut.mget.setLen(0)
|
|
||||||
lineFut.clean()
|
|
||||||
await client.recvLineInto(lineFut, maxLength=maxLine)
|
|
||||||
|
|
||||||
if lineFut.mget == "":
|
|
||||||
client.close(); return
|
|
||||||
if lineFut.mget.len > maxLine:
|
|
||||||
await request.respondError(Http413)
|
|
||||||
client.close(); return
|
|
||||||
if lineFut.mget == "\c\L": break
|
|
||||||
let (key, value) = parseHeader(lineFut.mget)
|
|
||||||
request.headers[key] = value
|
|
||||||
# Ensure the client isn't trying to DoS us.
|
|
||||||
if request.headers.len > headerLimit:
|
|
||||||
await client.sendStatus("400 Bad Request")
|
|
||||||
request.client.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
if request.reqMethod == HttpPost:
|
|
||||||
# Check for Expect header
|
|
||||||
if request.headers.hasKey("Expect"):
|
|
||||||
if "100-continue" in request.headers["Expect"]:
|
|
||||||
await client.sendStatus("100 Continue")
|
|
||||||
else:
|
|
||||||
await client.sendStatus("417 Expectation Failed")
|
|
||||||
|
|
||||||
# Read the body
|
|
||||||
# - Check for Content-length header
|
|
||||||
if request.headers.hasKey("Content-Length"):
|
|
||||||
var contentLength = 0
|
|
||||||
if parseInt(request.headers["Content-Length"],
|
|
||||||
contentLength) == 0:
|
|
||||||
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
request.body = await client.recv(contentLength)
|
|
||||||
if request.body.len != contentLength:
|
|
||||||
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
|
||||||
continue
|
|
||||||
elif request.reqMethod == HttpPost:
|
|
||||||
await request.respond(Http411, "Content-Length required.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Call the user's callback.
|
|
||||||
await callback(request)
|
|
||||||
|
|
||||||
if "upgrade" in request.headers.getOrDefault("connection"):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Persistent connections
|
if lineFut.mget.len > maxLine:
|
||||||
if (request.protocol == HttpVer11 and
|
await request.respondError(Http413)
|
||||||
request.headers.getOrDefault("connection").normalize != "close") or
|
client.close()
|
||||||
(request.protocol == HttpVer10 and
|
return
|
||||||
request.headers.getOrDefault("connection").normalize == "keep-alive"):
|
if lineFut.mget != "\c\L":
|
||||||
# In HTTP 1.1 we assume that connection is persistent. Unless connection
|
|
||||||
# header states otherwise.
|
|
||||||
# In HTTP 1.0 we assume that the connection should not be persistent.
|
|
||||||
# Unless the connection header states otherwise.
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
request.client.close()
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# First line - GET /path HTTP/1.1
|
||||||
|
var i = 0
|
||||||
|
for linePart in lineFut.mget.split(' '):
|
||||||
|
case i
|
||||||
|
of 0:
|
||||||
|
try:
|
||||||
|
# TODO: this is likely slow.
|
||||||
|
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
|
||||||
|
except ValueError:
|
||||||
|
asyncCheck request.respondError(Http400)
|
||||||
|
return
|
||||||
|
of 1:
|
||||||
|
try:
|
||||||
|
parseUri(linePart, request.url)
|
||||||
|
except ValueError:
|
||||||
|
asyncCheck request.respondError(Http400)
|
||||||
|
return
|
||||||
|
of 2:
|
||||||
|
try:
|
||||||
|
request.protocol = parseProtocol(linePart)
|
||||||
|
except ValueError:
|
||||||
|
asyncCheck request.respondError(Http400)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
await request.respondError(Http400)
|
||||||
|
return
|
||||||
|
inc i
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
while true:
|
||||||
|
i = 0
|
||||||
|
lineFut.mget.setLen(0)
|
||||||
|
lineFut.clean()
|
||||||
|
await client.recvLineInto(lineFut, maxLength=maxLine)
|
||||||
|
|
||||||
|
if lineFut.mget == "":
|
||||||
|
client.close(); return
|
||||||
|
if lineFut.mget.len > maxLine:
|
||||||
|
await request.respondError(Http413)
|
||||||
|
client.close(); return
|
||||||
|
if lineFut.mget == "\c\L": break
|
||||||
|
let (key, value) = parseHeader(lineFut.mget)
|
||||||
|
request.headers[key] = value
|
||||||
|
# Ensure the client isn't trying to DoS us.
|
||||||
|
if request.headers.len > headerLimit:
|
||||||
|
await client.sendStatus("400 Bad Request")
|
||||||
|
request.client.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
if request.reqMethod == HttpPost:
|
||||||
|
# Check for Expect header
|
||||||
|
if request.headers.hasKey("Expect"):
|
||||||
|
if "100-continue" in request.headers["Expect"]:
|
||||||
|
await client.sendStatus("100 Continue")
|
||||||
|
else:
|
||||||
|
await client.sendStatus("417 Expectation Failed")
|
||||||
|
|
||||||
|
# Read the body
|
||||||
|
# - Check for Content-length header
|
||||||
|
if request.headers.hasKey("Content-Length"):
|
||||||
|
var contentLength = 0
|
||||||
|
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
|
||||||
|
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if contentLength > server.maxBody:
|
||||||
|
await request.respondError(Http413)
|
||||||
|
return
|
||||||
|
request.body = await client.recv(contentLength)
|
||||||
|
if request.body.len != contentLength:
|
||||||
|
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
||||||
|
return
|
||||||
|
elif request.reqMethod == HttpPost:
|
||||||
|
await request.respond(Http411, "Content-Length required.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Call the user's callback.
|
||||||
|
await callback(request)
|
||||||
|
|
||||||
|
if "upgrade" in request.headers.getOrDefault("connection"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Persistent connections
|
||||||
|
if (request.protocol == HttpVer11 and
|
||||||
|
cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
|
||||||
|
(request.protocol == HttpVer10 and
|
||||||
|
cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
|
||||||
|
# In HTTP 1.1 we assume that connection is persistent. Unless connection
|
||||||
|
# header states otherwise.
|
||||||
|
# In HTTP 1.0 we assume that the connection should not be persistent.
|
||||||
|
# Unless the connection header states otherwise.
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
request.client.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
|
||||||
|
callback: proc (request: Request):
|
||||||
|
Future[void] {.closure, gcsafe.}) {.async.} =
|
||||||
|
var request = newFutureVar[Request]("asynchttpserver.processClient")
|
||||||
|
request.mget().url = initUri()
|
||||||
|
request.mget().headers = newHttpHeaders()
|
||||||
|
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
|
||||||
|
lineFut.mget() = newStringOfCap(80)
|
||||||
|
|
||||||
|
while not client.isClosed:
|
||||||
|
try:
|
||||||
|
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.},
|
||||||
address = "") {.async.} =
|
address = "") {.async.} =
|
||||||
|
|
@ -280,7 +299,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
|
||||||
# TODO: Causes compiler crash.
|
# TODO: Causes compiler crash.
|
||||||
#var (address, client) = await server.socket.acceptAddr()
|
#var (address, client) = await server.socket.acceptAddr()
|
||||||
var fut = await server.socket.acceptAddr()
|
var fut = await server.socket.acceptAddr()
|
||||||
asyncCheck processClient(fut.client, fut.address, callback)
|
asyncCheck processClient(server, fut.client, fut.address, callback)
|
||||||
#echo(f.isNil)
|
#echo(f.isNil)
|
||||||
#echo(f.repr)
|
#echo(f.repr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -472,27 +472,13 @@ proc stripAwait(node: NimNode): NimNode =
|
||||||
proc splitParamType(paramType: NimNode, async: bool): NimNode =
|
proc splitParamType(paramType: NimNode, async: bool): NimNode =
|
||||||
result = paramType
|
result = paramType
|
||||||
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
|
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
|
||||||
let firstType = paramType[1]
|
let firstAsync = "async" in ($paramType[1].ident).normalize
|
||||||
let firstTypeName = $firstType.ident
|
let secondAsync = "async" in ($paramType[2].ident).normalize
|
||||||
let secondType = paramType[2]
|
|
||||||
let secondTypeName = $secondType.ident
|
|
||||||
|
|
||||||
# Make sure that at least one has the name `async`, otherwise we shouldn't
|
if firstAsync:
|
||||||
# touch it.
|
result = paramType[if async: 1 else: 2]
|
||||||
if not ("async" in firstTypeName.normalize or
|
elif secondAsync:
|
||||||
"async" in secondTypeName.normalize):
|
result = paramType[if async: 2 else: 1]
|
||||||
return
|
|
||||||
|
|
||||||
if async:
|
|
||||||
if firstTypeName.normalize.startsWith("async"):
|
|
||||||
result = paramType[1]
|
|
||||||
elif secondTypeName.normalize.startsWith("async"):
|
|
||||||
result = paramType[2]
|
|
||||||
else:
|
|
||||||
if not firstTypeName.normalize.startsWith("async"):
|
|
||||||
result = paramType[1]
|
|
||||||
elif not secondTypeName.normalize.startsWith("async"):
|
|
||||||
result = paramType[2]
|
|
||||||
|
|
||||||
proc stripReturnType(returnType: NimNode): NimNode =
|
proc stripReturnType(returnType: NimNode): NimNode =
|
||||||
# Strip out the 'Future' from 'Future[T]'.
|
# Strip out the 'Future' from 'Future[T]'.
|
||||||
|
|
@ -535,4 +521,4 @@ macro multisync*(prc: untyped): untyped =
|
||||||
let (sync, asyncPrc) = splitProc(prc)
|
let (sync, asyncPrc) = splitProc(prc)
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
result.add(asyncSingleProc(asyncPrc))
|
result.add(asyncSingleProc(asyncPrc))
|
||||||
result.add(sync)
|
result.add(sync)
|
||||||
|
|
|
||||||
|
|
@ -21,24 +21,18 @@ proc openDefaultBrowser*(url: string) =
|
||||||
## opens `url` with the user's default browser. This does not block.
|
## opens `url` with the user's default browser. This does not block.
|
||||||
##
|
##
|
||||||
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
|
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
|
||||||
## command is used. Under Unix, it is checked if ``gnome-open`` exists and
|
## command is used. Under Unix, it is checked if ``xdg-open`` exists and
|
||||||
## used if it does. Next attempt is ``kde-open``, then ``xdg-open``.
|
## used if it does. Otherwise the environment variable ``BROWSER`` is
|
||||||
## Otherwise the environment variable ``BROWSER`` is used to determine the
|
## used to determine the default browser to use.
|
||||||
## default browser to use.
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
when useWinUnicode:
|
var o = newWideCString("open")
|
||||||
var o = newWideCString("open")
|
var u = newWideCString(url)
|
||||||
var u = newWideCString(url)
|
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
|
||||||
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
|
|
||||||
else:
|
|
||||||
discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
|
|
||||||
elif defined(macosx):
|
elif defined(macosx):
|
||||||
discard execShellCmd("open " & quoteShell(url))
|
discard execShellCmd("open " & quoteShell(url))
|
||||||
else:
|
else:
|
||||||
const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
|
|
||||||
var u = quoteShell(url)
|
var u = quoteShell(url)
|
||||||
for a in items(attempts):
|
if execShellCmd("xdg-open " & u) == 0: return
|
||||||
if execShellCmd(a & u) == 0: return
|
|
||||||
for b in getEnv("BROWSER").string.split(PathSep):
|
for b in getEnv("BROWSER").string.split(PathSep):
|
||||||
try:
|
try:
|
||||||
# we use ``startProcess`` here because we don't want to block!
|
# we use ``startProcess`` here because we don't want to block!
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
||||||
|
|
@ -704,6 +706,52 @@ template newSeqWith*(len: int, init: untyped): untyped =
|
||||||
result[i] = init
|
result[i] = init
|
||||||
result
|
result
|
||||||
|
|
||||||
|
proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
|
||||||
|
filter = nnkLiterals): NimNode =
|
||||||
|
if constructor.kind in filter:
|
||||||
|
result = newNimNode(nnkCall, lineInfoFrom=constructor)
|
||||||
|
result.add op
|
||||||
|
result.add constructor
|
||||||
|
else:
|
||||||
|
result = newNimNode(constructor.kind, lineInfoFrom=constructor)
|
||||||
|
for v in constructor:
|
||||||
|
if nested or v.kind in filter:
|
||||||
|
result.add mapLitsImpl(v, op, nested, filter)
|
||||||
|
else:
|
||||||
|
result.add v
|
||||||
|
|
||||||
|
macro mapLiterals*(constructor, op: untyped;
|
||||||
|
nested = true): untyped =
|
||||||
|
## applies ``op`` to each of the **atomic** literals like ``3``
|
||||||
|
## or ``"abc"`` in the specified ``constructor`` AST. This can
|
||||||
|
## be used to map every array element to some target type:
|
||||||
|
##
|
||||||
|
## Example:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
|
||||||
|
## doAssert x is array[4, int]
|
||||||
|
##
|
||||||
|
## Short notation for:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
|
||||||
|
##
|
||||||
|
## If ``nested`` is true, the literals are replaced everywhere
|
||||||
|
## in the ``constructor`` AST, otherwise only the first level
|
||||||
|
## is considered:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## mapLiterals((1, ("abc"), 2), float, nested=false)
|
||||||
|
##
|
||||||
|
## Produces::
|
||||||
|
##
|
||||||
|
## (float(1), ("abc"), float(2))
|
||||||
|
##
|
||||||
|
## There are no constraints for the ``constructor`` AST, it
|
||||||
|
## works for nested tuples of arrays of sets etc.
|
||||||
|
result = mapLitsImpl(constructor, op, nested.boolVal)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import strutils
|
import strutils
|
||||||
block: # concat test
|
block: # concat test
|
||||||
|
|
@ -992,5 +1040,11 @@ when isMainModule:
|
||||||
seq2D[0][1] = true
|
seq2D[0][1] = true
|
||||||
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
|
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
|
||||||
|
|
||||||
|
block: # mapLiterals tests
|
||||||
|
let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
|
||||||
|
doAssert x is array[4, int]
|
||||||
|
doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
|
||||||
|
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
|
||||||
|
|
||||||
when not defined(testing):
|
when not defined(testing):
|
||||||
echo "Finished doc tests"
|
echo "Finished doc tests"
|
||||||
|
|
|
||||||
|
|
@ -87,10 +87,10 @@ proc newSharedString*(s: string): SharedString =
|
||||||
result.len = len
|
result.len = len
|
||||||
|
|
||||||
when declared(atomicLoadN):
|
when declared(atomicLoadN):
|
||||||
template load(x): expr = atomicLoadN(addr x, ATOMIC_SEQ_CST)
|
template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
|
||||||
else:
|
else:
|
||||||
# XXX Fixme
|
# XXX Fixme
|
||||||
template load(x): expr = x
|
template load(x): untyped = x
|
||||||
|
|
||||||
proc add*(s: var SharedString; t: cstring; len: Natural) =
|
proc add*(s: var SharedString; t: cstring; len: Natural) =
|
||||||
if len == 0: return
|
if len == 0: return
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,15 @@ import strtabs, times
|
||||||
|
|
||||||
proc parseCookies*(s: string): StringTableRef =
|
proc parseCookies*(s: string): StringTableRef =
|
||||||
## parses cookies into a string table.
|
## parses cookies into a string table.
|
||||||
|
##
|
||||||
|
## The proc is meant to parse the Cookie header set by a client, not the
|
||||||
|
## "Set-Cookie" header set by servers.
|
||||||
|
##
|
||||||
|
## Example:
|
||||||
|
##
|
||||||
|
## .. code-block::Nim
|
||||||
|
## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
|
||||||
|
|
||||||
result = newStringTable(modeCaseInsensitive)
|
result = newStringTable(modeCaseInsensitive)
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
|
||||||
|
|
@ -197,4 +197,4 @@ macro dump*(x: typed): untyped =
|
||||||
let s = x.toStrLit
|
let s = x.toStrLit
|
||||||
let r = quote do:
|
let r = quote do:
|
||||||
debugEcho `s`, " = ", `x`
|
debugEcho `s`, " = ", `x`
|
||||||
return r
|
return r
|
||||||
|
|
|
||||||
|
|
@ -1,211 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## The ``gentabs`` module implements an efficient hash table that is a
|
|
||||||
## key-value mapping. The keys are required to be strings, but the values
|
|
||||||
## may be any Nim or user defined type. This module supports matching
|
|
||||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
|
||||||
##
|
|
||||||
## **Warning:** This module is deprecated, new code shouldn't use it!
|
|
||||||
|
|
||||||
{.deprecated.}
|
|
||||||
|
|
||||||
import
|
|
||||||
os, hashes, strutils
|
|
||||||
|
|
||||||
type
|
|
||||||
GenTableMode* = enum ## describes the table's key matching mode
|
|
||||||
modeCaseSensitive, ## case sensitive matching of keys
|
|
||||||
modeCaseInsensitive, ## case insensitive matching of keys
|
|
||||||
modeStyleInsensitive ## style sensitive matching of keys
|
|
||||||
|
|
||||||
GenKeyValuePair[T] = tuple[key: string, val: T]
|
|
||||||
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
|
|
||||||
GenTable*[T] = object of RootObj
|
|
||||||
counter: int
|
|
||||||
data: GenKeyValuePairSeq[T]
|
|
||||||
mode: GenTableMode
|
|
||||||
|
|
||||||
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
|
|
||||||
|
|
||||||
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
|
|
||||||
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
|
|
||||||
|
|
||||||
const
|
|
||||||
growthFactor = 2
|
|
||||||
startSize = 64
|
|
||||||
|
|
||||||
|
|
||||||
proc len*[T](tbl: PGenTable[T]): int {.inline.} =
|
|
||||||
## returns the number of keys in `tbl`.
|
|
||||||
result = tbl.counter
|
|
||||||
|
|
||||||
iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
|
|
||||||
## iterates over any (key, value) pair in the table `tbl`.
|
|
||||||
for h in 0..high(tbl.data):
|
|
||||||
if not isNil(tbl.data[h].key):
|
|
||||||
yield (tbl.data[h].key, tbl.data[h].val)
|
|
||||||
|
|
||||||
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
|
|
||||||
case tbl.mode
|
|
||||||
of modeCaseSensitive: result = hashes.hash(key)
|
|
||||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
|
||||||
of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
|
|
||||||
|
|
||||||
proc myCmp[T](tbl: PGenTable[T], a, b: string): bool =
|
|
||||||
case tbl.mode
|
|
||||||
of modeCaseSensitive: result = cmp(a, b) == 0
|
|
||||||
of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
|
|
||||||
of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
|
|
||||||
|
|
||||||
proc mustRehash(length, counter: int): bool =
|
|
||||||
assert(length > counter)
|
|
||||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
|
||||||
|
|
||||||
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
|
|
||||||
## creates a new generic hash table that is empty.
|
|
||||||
new(result)
|
|
||||||
result.mode = mode
|
|
||||||
result.counter = 0
|
|
||||||
newSeq(result.data, startSize)
|
|
||||||
|
|
||||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
|
||||||
result = ((5 * h) + 1) and maxHash
|
|
||||||
|
|
||||||
proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
|
||||||
var h: Hash
|
|
||||||
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
|
|
||||||
while not isNil(tbl.data[h].key):
|
|
||||||
if myCmp(tbl, tbl.data[h].key, key):
|
|
||||||
return h
|
|
||||||
h = nextTry(h, high(tbl.data))
|
|
||||||
result = - 1
|
|
||||||
|
|
||||||
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
|
|
||||||
key: string, val: T) =
|
|
||||||
var h: Hash
|
|
||||||
h = myhash(tbl, key) and high(data)
|
|
||||||
while not isNil(data[h].key):
|
|
||||||
h = nextTry(h, high(data))
|
|
||||||
data[h].key = key
|
|
||||||
data[h].val = val
|
|
||||||
|
|
||||||
proc enlarge[T](tbl: PGenTable[T]) =
|
|
||||||
var n: GenKeyValuePairSeq[T]
|
|
||||||
newSeq(n, len(tbl.data) * growthFactor)
|
|
||||||
for i in countup(0, high(tbl.data)):
|
|
||||||
if not isNil(tbl.data[i].key):
|
|
||||||
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
|
|
||||||
swap(tbl.data, n)
|
|
||||||
|
|
||||||
proc hasKey*[T](tbl: PGenTable[T], key: string): bool =
|
|
||||||
## returns true iff `key` is in the table `tbl`.
|
|
||||||
result = rawGet(tbl, key) >= 0
|
|
||||||
|
|
||||||
proc `[]`*[T](tbl: PGenTable[T], key: string): T =
|
|
||||||
## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`,
|
|
||||||
## default(T) is returned and no exception is raised. One can check
|
|
||||||
## with ``hasKey`` whether the key exists.
|
|
||||||
var index = rawGet(tbl, key)
|
|
||||||
if index >= 0: result = tbl.data[index].val
|
|
||||||
|
|
||||||
proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) =
|
|
||||||
## puts a (key, value)-pair into `tbl`.
|
|
||||||
var index = rawGet(tbl, key)
|
|
||||||
if index >= 0:
|
|
||||||
tbl.data[index].val = val
|
|
||||||
else:
|
|
||||||
if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl)
|
|
||||||
rawInsert(tbl, tbl.data, key, val)
|
|
||||||
inc(tbl.counter)
|
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
#
|
|
||||||
# Verify tables of integer values (string keys)
|
|
||||||
#
|
|
||||||
var x = newGenTable[int](modeCaseInsensitive)
|
|
||||||
x["one"] = 1
|
|
||||||
x["two"] = 2
|
|
||||||
x["three"] = 3
|
|
||||||
x["four"] = 4
|
|
||||||
x["five"] = 5
|
|
||||||
assert(len(x) == 5) # length procedure works
|
|
||||||
assert(x["one"] == 1) # case-sensitive lookup works
|
|
||||||
assert(x["ONE"] == 1) # case-insensitive should work for this table
|
|
||||||
assert(x["one"]+x["two"] == 3) # make sure we're getting back ints
|
|
||||||
assert(x.hasKey("one")) # hasKey should return 'true' for a key
|
|
||||||
# of "one"...
|
|
||||||
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
|
||||||
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
|
||||||
assert(x[k]==v)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify a table of user-defined types
|
|
||||||
#
|
|
||||||
type
|
|
||||||
MyType = tuple[first, second: string] # a pair of strings
|
|
||||||
{.deprecated: [TMyType: MyType].}
|
|
||||||
|
|
||||||
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
|
|
||||||
# value is MyType tuple
|
|
||||||
|
|
||||||
#var junk: MyType = ("OK", "Here")
|
|
||||||
|
|
||||||
#echo junk.first, " ", junk.second
|
|
||||||
|
|
||||||
y["Hello"] = ("Hello", "World")
|
|
||||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
|
||||||
#y["Hello"] = MyType( ("Hello", "World") )
|
|
||||||
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
|
|
||||||
|
|
||||||
assert( not isNil(y["Hello"].first) )
|
|
||||||
assert( y["Hello"].first == "Hello" )
|
|
||||||
assert( y["Hello"].second == "World" )
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify table of tables
|
|
||||||
#
|
|
||||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
|
||||||
# a hash table of ints
|
|
||||||
|
|
||||||
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
|
||||||
z["first"] = newGenTable[int](modeCaseInsensitive)
|
|
||||||
z["first"]["one"] = 1
|
|
||||||
z["first"]["two"] = 2
|
|
||||||
z["first"]["three"] = 3
|
|
||||||
|
|
||||||
z["second"] = newGenTable[int](modeCaseInsensitive)
|
|
||||||
z["second"]["red"] = 10
|
|
||||||
z["second"]["blue"] = 20
|
|
||||||
|
|
||||||
assert(len(z) == 2) # length of outer table
|
|
||||||
assert(len(z["first"]) == 3) # length of "first" table
|
|
||||||
assert(len(z["second"]) == 2) # length of "second" table
|
|
||||||
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
|
||||||
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
|
||||||
|
|
||||||
when false:
|
|
||||||
# disabled: depends on hash order:
|
|
||||||
var output = ""
|
|
||||||
for k, v in pairs(z):
|
|
||||||
output.add( "$# ($#) ->\L" % [k,$len(v)] )
|
|
||||||
for k2,v2 in pairs(v):
|
|
||||||
output.add( " $# <-> $#\L" % [k2,$v2] )
|
|
||||||
|
|
||||||
let expected = unindent """
|
|
||||||
first (3) ->
|
|
||||||
two <-> 2
|
|
||||||
three <-> 3
|
|
||||||
one <-> 1
|
|
||||||
second (2) ->
|
|
||||||
red <-> 10
|
|
||||||
blue <-> 20
|
|
||||||
"""
|
|
||||||
assert output == expected
|
|
||||||
|
|
@ -43,6 +43,7 @@ const bsdPlatform = defined(macosx) or defined(freebsd) or
|
||||||
defined(dragonfly)
|
defined(dragonfly)
|
||||||
|
|
||||||
when defined(nimdoc):
|
when defined(nimdoc):
|
||||||
|
type SocketHandle = int
|
||||||
type
|
type
|
||||||
Selector*[T] = ref object
|
Selector*[T] = ref object
|
||||||
## An object which holds descriptors to be checked for read/write status
|
## An object which holds descriptors to be checked for read/write status
|
||||||
|
|
|
||||||
|
|
@ -1849,8 +1849,8 @@ when isMainModule:
|
||||||
discard parseJson"""{ invalid"""
|
discard parseJson"""{ invalid"""
|
||||||
except:
|
except:
|
||||||
discard
|
discard
|
||||||
# memory diff should less than 2M
|
# memory diff should less than 4M
|
||||||
doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024)
|
doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
|
||||||
|
|
||||||
|
|
||||||
# test `$`
|
# test `$`
|
||||||
|
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2013 Robert Persson
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## **Warning:** This module will be moved out of the stdlib and into a
|
|
||||||
## Nimble package, don't use it.
|
|
||||||
|
|
||||||
type OneVarFunction* = proc (x: float): float
|
|
||||||
|
|
||||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
|
||||||
|
|
||||||
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
|
||||||
tuple[rootx, rooty: float, success: bool]=
|
|
||||||
## Searches `function` for a root between `xmin` and `xmax`
|
|
||||||
## using brents method. If the function value at `xmin`and `xmax` has the
|
|
||||||
## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
|
|
||||||
## and succes is set to false.
|
|
||||||
## Otherwise there exists at least one root and success is set to true.
|
|
||||||
## This root is searched for at most `maxiter` iterations.
|
|
||||||
## If `tol` tolerance is reached within `maxiter` iterations
|
|
||||||
## the root refinement stops and success=true.
|
|
||||||
|
|
||||||
# see http://en.wikipedia.org/wiki/Brent%27s_method
|
|
||||||
var
|
|
||||||
a=xmin
|
|
||||||
b=xmax
|
|
||||||
c=a
|
|
||||||
d=1.0e308
|
|
||||||
fa=function(a)
|
|
||||||
fb=function(b)
|
|
||||||
fc=fa
|
|
||||||
s=0.0
|
|
||||||
fs=0.0
|
|
||||||
mflag:bool
|
|
||||||
i=0
|
|
||||||
tmp2:float
|
|
||||||
|
|
||||||
if fa*fb>=0:
|
|
||||||
if abs(fa)<abs(fb):
|
|
||||||
return (a,fa,false)
|
|
||||||
else:
|
|
||||||
return (b,fb,false)
|
|
||||||
|
|
||||||
if abs(fa)<abs(fb):
|
|
||||||
swap(fa,fb)
|
|
||||||
swap(a,b)
|
|
||||||
|
|
||||||
while fb!=0.0 and abs(a-b)>tol:
|
|
||||||
if fa!=fc and fb!=fc: # inverse quadratic interpolation
|
|
||||||
s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
|
|
||||||
else: #secant rule
|
|
||||||
s = b - fb * (b - a) / (fb - fa)
|
|
||||||
tmp2 = (3.0 * a + b) / 4.0
|
|
||||||
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
|
|
||||||
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
|
|
||||||
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
|
|
||||||
s=(a+b)/2.0
|
|
||||||
mflag=true
|
|
||||||
else:
|
|
||||||
if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
|
|
||||||
s=(a+b)/2.0
|
|
||||||
mflag=true
|
|
||||||
else:
|
|
||||||
mflag=false
|
|
||||||
fs = function(s)
|
|
||||||
d = c
|
|
||||||
c = b
|
|
||||||
fc = fb
|
|
||||||
if fa * fs<0.0:
|
|
||||||
b=s
|
|
||||||
fb=fs
|
|
||||||
else:
|
|
||||||
a=s
|
|
||||||
fa=fs
|
|
||||||
if abs(fa)<abs(fb):
|
|
||||||
swap(a,b)
|
|
||||||
swap(fa,fb)
|
|
||||||
inc i
|
|
||||||
if i>maxiter:
|
|
||||||
break
|
|
||||||
|
|
||||||
return (b,fb,true)
|
|
||||||
|
|
@ -1195,14 +1195,15 @@ when defined(nimdoc):
|
||||||
## Returns the number of `command line arguments`:idx: given to the
|
## Returns the number of `command line arguments`:idx: given to the
|
||||||
## application.
|
## application.
|
||||||
##
|
##
|
||||||
## If your binary was called without parameters this will return zero. You
|
## Unlike `argc`:idx: in C, if your binary was called without parameters this
|
||||||
## can later query each individual paramater with `paramStr() <#paramStr>`_
|
## will return zero.
|
||||||
|
## You can query each individual paramater with `paramStr() <#paramStr>`_
|
||||||
## or retrieve all of them in one go with `commandLineParams()
|
## or retrieve all of them in one go with `commandLineParams()
|
||||||
## <#commandLineParams>`_.
|
## <#commandLineParams>`_.
|
||||||
##
|
##
|
||||||
## **Availability**: On Posix there is no portable way to get the command
|
## **Availability**: When generating a dynamic library (see --app:lib) on
|
||||||
## line from a DLL and thus the proc isn't defined in this environment. You
|
## Posix this proc is not defined.
|
||||||
## can test for its availability with `declared() <system.html#declared>`_.
|
## Test for availability using `declared() <system.html#declared>`_.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
|
|
@ -1219,13 +1220,14 @@ when defined(nimdoc):
|
||||||
## `paramCount() <#paramCount>`_ with this proc you can call the
|
## `paramCount() <#paramCount>`_ with this proc you can call the
|
||||||
## convenience `commandLineParams() <#commandLineParams>`_.
|
## convenience `commandLineParams() <#commandLineParams>`_.
|
||||||
##
|
##
|
||||||
## It is possible to call ``paramStr(0)`` but this will return OS specific
|
## Similarly to `argv`:idx: in C,
|
||||||
|
## it is possible to call ``paramStr(0)`` but this will return OS specific
|
||||||
## contents (usually the name of the invoked executable). You should avoid
|
## contents (usually the name of the invoked executable). You should avoid
|
||||||
## this and call `getAppFilename() <#getAppFilename>`_ instead.
|
## this and call `getAppFilename() <#getAppFilename>`_ instead.
|
||||||
##
|
##
|
||||||
## **Availability**: On Posix there is no portable way to get the command
|
## **Availability**: When generating a dynamic library (see --app:lib) on
|
||||||
## line from a DLL and thus the proc isn't defined in this environment. You
|
## Posix this proc is not defined.
|
||||||
## can test for its availability with `declared() <system.html#declared>`_.
|
## Test for availability using `declared() <system.html#declared>`_.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -841,7 +841,7 @@ elif not defined(useNimRtl):
|
||||||
var attr: Tposix_spawnattr
|
var attr: Tposix_spawnattr
|
||||||
var fops: Tposix_spawn_file_actions
|
var fops: Tposix_spawn_file_actions
|
||||||
|
|
||||||
template chck(e: expr) =
|
template chck(e: untyped) =
|
||||||
if e != 0'i32: raiseOSError(osLastError())
|
if e != 0'i32: raiseOSError(osLastError())
|
||||||
|
|
||||||
chck posix_spawn_file_actions_init(fops)
|
chck posix_spawn_file_actions_init(fops)
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||||
elif result != 0:
|
elif result != 0:
|
||||||
number = int(res)
|
number = int(res)
|
||||||
|
|
||||||
|
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
|
||||||
|
## parses a natural number into ``b``. This cannot raise an overflow
|
||||||
|
## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
|
||||||
|
## The number of processed character is returned.
|
||||||
|
## This is usually what you really want to use instead of `parseInt`:idx:.
|
||||||
|
## Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## var res = 0
|
||||||
|
## discard parseSaturatedNatural("848", res)
|
||||||
|
## doAssert res == 848
|
||||||
|
var i = start
|
||||||
|
if s[i] == '+': inc(i)
|
||||||
|
if s[i] in {'0'..'9'}:
|
||||||
|
b = 0
|
||||||
|
while s[i] in {'0'..'9'}:
|
||||||
|
let c = ord(s[i]) - ord('0')
|
||||||
|
if b <= (high(int) - c) div 10:
|
||||||
|
b = b * 10 + c
|
||||||
|
else:
|
||||||
|
b = high(int)
|
||||||
|
inc(i)
|
||||||
|
while s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||||
|
result = i - start
|
||||||
|
|
||||||
# overflowChecks doesn't work with BiggestUInt
|
# overflowChecks doesn't work with BiggestUInt
|
||||||
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
|
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
|
||||||
var
|
var
|
||||||
|
|
@ -393,16 +418,43 @@ when isMainModule:
|
||||||
let input = "$test{} $this is ${an{ example}} "
|
let input = "$test{} $this is ${an{ example}} "
|
||||||
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
|
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
|
||||||
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
|
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
|
||||||
assert toSeq(interpolatedFragments(input)) == expected
|
doAssert toSeq(interpolatedFragments(input)) == expected
|
||||||
|
|
||||||
var value = 0
|
var value = 0
|
||||||
discard parseHex("0x38", value)
|
discard parseHex("0x38", value)
|
||||||
assert value == 56
|
doAssert value == 56
|
||||||
discard parseHex("0x34", value)
|
discard parseHex("0x34", value)
|
||||||
assert value == 56 * 256 + 52
|
doAssert value == 56 * 256 + 52
|
||||||
value = -1
|
value = -1
|
||||||
discard parseHex("0x38", value)
|
discard parseHex("0x38", value)
|
||||||
assert value == -200
|
doAssert value == -200
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
doAssert(parseSaturatedNatural("848", value) == 3)
|
||||||
|
doAssert value == 848
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
|
||||||
|
doAssert value == high(int)
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
discard parseSaturatedNatural("9223372036854775808", value)
|
||||||
|
doAssert value == high(int)
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
discard parseSaturatedNatural("9223372036854775807", value)
|
||||||
|
doAssert value == high(int)
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
discard parseSaturatedNatural("18446744073709551616", value)
|
||||||
|
doAssert value == high(int)
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
discard parseSaturatedNatural("18446744073709551615", value)
|
||||||
|
doAssert value == high(int)
|
||||||
|
|
||||||
|
value = -1
|
||||||
|
doAssert(parseSaturatedNatural("1_000_000", value) == 9)
|
||||||
|
doAssert value == 1_000_000
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -1,371 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2013 Robert Persson
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## **Warning:** This module will be moved out of the stdlib and into a
|
|
||||||
## Nimble package, don't use it.
|
|
||||||
|
|
||||||
import math
|
|
||||||
import strutils
|
|
||||||
import numeric
|
|
||||||
|
|
||||||
type
|
|
||||||
Poly* = object
|
|
||||||
cofs:seq[float]
|
|
||||||
|
|
||||||
{.deprecated: [TPoly: Poly].}
|
|
||||||
|
|
||||||
proc degree*(p:Poly):int=
|
|
||||||
## Returns the degree of the polynomial,
|
|
||||||
## that is the number of coefficients-1
|
|
||||||
return p.cofs.len-1
|
|
||||||
|
|
||||||
|
|
||||||
proc eval*(p:Poly,x:float):float=
|
|
||||||
## Evaluates a polynomial function value for `x`
|
|
||||||
## quickly using Horners method
|
|
||||||
var n=p.degree
|
|
||||||
result=p.cofs[n]
|
|
||||||
dec n
|
|
||||||
while n>=0:
|
|
||||||
result = result*x+p.cofs[n]
|
|
||||||
dec n
|
|
||||||
|
|
||||||
proc `[]` *(p:Poly;idx:int):float=
|
|
||||||
## Gets a coefficient of the polynomial.
|
|
||||||
## p[2] will returns the quadric term, p[3] the cubic etc.
|
|
||||||
## Out of bounds index will return 0.0.
|
|
||||||
if idx<0 or idx>p.degree:
|
|
||||||
return 0.0
|
|
||||||
return p.cofs[idx]
|
|
||||||
|
|
||||||
proc `[]=` *(p:var Poly;idx:int,v:float)=
|
|
||||||
## Sets an coefficient of the polynomial by index.
|
|
||||||
## p[2] set the quadric term, p[3] the cubic etc.
|
|
||||||
## If index is out of range for the coefficients,
|
|
||||||
## the polynomial grows to the smallest needed degree.
|
|
||||||
assert(idx>=0)
|
|
||||||
|
|
||||||
if idx>p.degree: #polynomial must grow
|
|
||||||
var oldlen=p.cofs.len
|
|
||||||
p.cofs.setLen(idx+1)
|
|
||||||
for q in oldlen.. <high(p.cofs):
|
|
||||||
p.cofs[q]=0.0 #new-grown coefficients set to zero
|
|
||||||
|
|
||||||
p.cofs[idx]=v
|
|
||||||
|
|
||||||
|
|
||||||
iterator items*(p:Poly):float=
|
|
||||||
## Iterates through the coefficients of the polynomial.
|
|
||||||
var i=p.degree
|
|
||||||
while i>=0:
|
|
||||||
yield p[i]
|
|
||||||
dec i
|
|
||||||
|
|
||||||
proc clean*(p:var Poly;zerotol=0.0)=
|
|
||||||
## Removes leading zero coefficients of the polynomial.
|
|
||||||
## An optional tolerance can be given for what's considered zero.
|
|
||||||
var n=p.degree
|
|
||||||
var relen=false
|
|
||||||
|
|
||||||
while n>0 and abs(p[n])<=zerotol: # >0 => keep at least one coefficient
|
|
||||||
dec n
|
|
||||||
relen=true
|
|
||||||
|
|
||||||
if relen: p.cofs.setLen(n+1)
|
|
||||||
|
|
||||||
|
|
||||||
proc `$` *(p:Poly):string =
|
|
||||||
## Gets a somewhat reasonable string representation of the polynomial
|
|
||||||
## The format should be compatible with most online function plotters,
|
|
||||||
## for example directly in google search
|
|
||||||
result=""
|
|
||||||
var first=true #might skip + sign if first coefficient
|
|
||||||
|
|
||||||
for idx in countdown(p.degree,0):
|
|
||||||
let a=p[idx]
|
|
||||||
|
|
||||||
if a==0.0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if a>= 0.0 and not first:
|
|
||||||
result.add('+')
|
|
||||||
first=false
|
|
||||||
|
|
||||||
if a!=1.0 or idx==0:
|
|
||||||
result.add(formatFloat(a,ffDefault,0))
|
|
||||||
if idx>=2:
|
|
||||||
result.add("x^" & $idx)
|
|
||||||
elif idx==1:
|
|
||||||
result.add("x")
|
|
||||||
|
|
||||||
if result=="":
|
|
||||||
result="0"
|
|
||||||
|
|
||||||
|
|
||||||
proc derivative*(p: Poly): Poly=
|
|
||||||
## Returns a new polynomial, which is the derivative of `p`
|
|
||||||
newSeq[float](result.cofs,p.degree)
|
|
||||||
for idx in 0..high(result.cofs):
|
|
||||||
result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
|
|
||||||
|
|
||||||
proc diff*(p:Poly,x:float):float=
|
|
||||||
## Evaluates the differentiation of a polynomial with
|
|
||||||
## respect to `x` quickly using a modifed Horners method
|
|
||||||
var n=p.degree
|
|
||||||
result=p[n]*float(n)
|
|
||||||
dec n
|
|
||||||
while n>=1:
|
|
||||||
result = result*x+p[n]*float(n)
|
|
||||||
dec n
|
|
||||||
|
|
||||||
proc integral*(p:Poly):Poly=
|
|
||||||
## Returns a new polynomial which is the indefinite
|
|
||||||
## integral of `p`. The constant term is set to 0.0
|
|
||||||
newSeq(result.cofs,p.cofs.len+1)
|
|
||||||
result.cofs[0]=0.0 #constant arbitrary term, use 0.0
|
|
||||||
for i in 1..high(result.cofs):
|
|
||||||
result.cofs[i]=p.cofs[i-1]/float(i)
|
|
||||||
|
|
||||||
|
|
||||||
proc integrate*(p:Poly;xmin,xmax:float):float=
|
|
||||||
## Computes the definite integral of `p` between `xmin` and `xmax`
|
|
||||||
## quickly using a modified version of Horners method
|
|
||||||
var
|
|
||||||
n=p.degree
|
|
||||||
s1=p[n]/float(n+1)
|
|
||||||
s2=s1
|
|
||||||
fac:float
|
|
||||||
|
|
||||||
dec n
|
|
||||||
while n>=0:
|
|
||||||
fac=p[n]/float(n+1)
|
|
||||||
s1 = s1*xmin+fac
|
|
||||||
s2 = s2*xmax+fac
|
|
||||||
dec n
|
|
||||||
|
|
||||||
result=s2*xmax-s1*xmin
|
|
||||||
|
|
||||||
proc initPoly*(cofs:varargs[float]):Poly=
|
|
||||||
## Initializes a polynomial with given coefficients.
|
|
||||||
## The most significant coefficient is first, so to create x^2-2x+3:
|
|
||||||
## intiPoly(1.0,-2.0,3.0)
|
|
||||||
if len(cofs)<=0:
|
|
||||||
result.cofs= @[0.0] #need at least one coefficient
|
|
||||||
else:
|
|
||||||
# reverse order of coefficients so indexing matches degree of
|
|
||||||
# coefficient...
|
|
||||||
result.cofs= @[]
|
|
||||||
for idx in countdown(cofs.len-1,0):
|
|
||||||
result.cofs.add(cofs[idx])
|
|
||||||
|
|
||||||
result.clean #remove leading zero terms
|
|
||||||
|
|
||||||
|
|
||||||
proc divMod*(p,d:Poly;q,r:var Poly)=
|
|
||||||
## Divides `p` with `d`, and stores the quotinent in `q` and
|
|
||||||
## the remainder in `d`
|
|
||||||
var
|
|
||||||
pdeg=p.degree
|
|
||||||
ddeg=d.degree
|
|
||||||
power=p.degree-d.degree
|
|
||||||
ratio:float
|
|
||||||
|
|
||||||
r.cofs = p.cofs #initial remainder=numerator
|
|
||||||
if power<0: #denominator is larger than numerator
|
|
||||||
q.cofs= @ [0.0] #quotinent is 0.0
|
|
||||||
return # keep remainder as numerator
|
|
||||||
|
|
||||||
q.cofs=newSeq[float](power+1)
|
|
||||||
|
|
||||||
for i in countdown(pdeg,ddeg):
|
|
||||||
ratio=r.cofs[i]/d.cofs[ddeg]
|
|
||||||
|
|
||||||
q.cofs[i-ddeg]=ratio
|
|
||||||
r.cofs[i]=0.0
|
|
||||||
|
|
||||||
for j in countup(0,<ddeg):
|
|
||||||
var idx=i-ddeg+j
|
|
||||||
r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
|
|
||||||
|
|
||||||
r.clean # drop zero coefficients in remainder
|
|
||||||
|
|
||||||
proc `+` *(p1:Poly,p2:Poly):Poly=
|
|
||||||
## Adds two polynomials
|
|
||||||
var n=max(p1.cofs.len,p2.cofs.len)
|
|
||||||
newSeq(result.cofs,n)
|
|
||||||
|
|
||||||
for idx in countup(0,n-1):
|
|
||||||
result[idx]=p1[idx]+p2[idx]
|
|
||||||
|
|
||||||
result.clean # drop zero coefficients in remainder
|
|
||||||
|
|
||||||
proc `*` *(p1:Poly,p2:Poly):Poly=
|
|
||||||
## Multiplies the polynomial `p1` with `p2`
|
|
||||||
var
|
|
||||||
d1=p1.degree
|
|
||||||
d2=p2.degree
|
|
||||||
n=d1+d2
|
|
||||||
idx:int
|
|
||||||
|
|
||||||
newSeq(result.cofs,n)
|
|
||||||
|
|
||||||
for i1 in countup(0,d1):
|
|
||||||
for i2 in countup(0,d2):
|
|
||||||
idx=i1+i2
|
|
||||||
result[idx]=result[idx]+p1[i1]*p2[i2]
|
|
||||||
|
|
||||||
result.clean
|
|
||||||
|
|
||||||
proc `*` *(p:Poly,f:float):Poly=
|
|
||||||
## Multiplies the polynomial `p` with a real number
|
|
||||||
newSeq(result.cofs,p.cofs.len)
|
|
||||||
for i in 0..high(p.cofs):
|
|
||||||
result[i]=p.cofs[i]*f
|
|
||||||
result.clean
|
|
||||||
|
|
||||||
proc `*` *(f:float,p:Poly):Poly=
|
|
||||||
## Multiplies a real number with a polynomial
|
|
||||||
return p*f
|
|
||||||
|
|
||||||
proc `-`*(p:Poly):Poly=
|
|
||||||
## Negates a polynomial
|
|
||||||
result=p
|
|
||||||
for i in countup(0,<result.cofs.len):
|
|
||||||
result.cofs[i]= -result.cofs[i]
|
|
||||||
|
|
||||||
proc `-` *(p1:Poly,p2:Poly):Poly=
|
|
||||||
## Subtract `p1` with `p2`
|
|
||||||
var n=max(p1.cofs.len,p2.cofs.len)
|
|
||||||
newSeq(result.cofs,n)
|
|
||||||
|
|
||||||
for idx in countup(0,n-1):
|
|
||||||
result[idx]=p1[idx]-p2[idx]
|
|
||||||
|
|
||||||
result.clean # drop zero coefficients in remainder
|
|
||||||
|
|
||||||
proc `/`*(p:Poly,f:float):Poly=
|
|
||||||
## Divides polynomial `p` with a real number `f`
|
|
||||||
newSeq(result.cofs,p.cofs.len)
|
|
||||||
for i in 0..high(p.cofs):
|
|
||||||
result[i]=p.cofs[i]/f
|
|
||||||
result.clean
|
|
||||||
|
|
||||||
proc `/` *(p,q:Poly):Poly=
|
|
||||||
## Divides polynomial `p` with polynomial `q`
|
|
||||||
var dummy:Poly
|
|
||||||
p.divMod(q,result,dummy)
|
|
||||||
|
|
||||||
proc `mod` *(p,q:Poly):Poly=
|
|
||||||
## Computes the polynomial modulo operation,
|
|
||||||
## that is the remainder of `p`/`q`
|
|
||||||
var dummy:Poly
|
|
||||||
p.divMod(q,dummy,result)
|
|
||||||
|
|
||||||
|
|
||||||
proc normalize*(p:var Poly)=
|
|
||||||
## Multiplies the polynomial inplace by a term so that
|
|
||||||
## the leading term is 1.0.
|
|
||||||
## This might lead to an unstable polynomial
|
|
||||||
## if the leading term is zero.
|
|
||||||
p=p/p[p.degree]
|
|
||||||
|
|
||||||
|
|
||||||
proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
|
|
||||||
## Solves the quadric equation `ax^2+bx+c`, with a possible
|
|
||||||
## tolerance `zerotol` to find roots of curves just 'touching'
|
|
||||||
## the x axis. Returns sequence with 0,1 or 2 solutions.
|
|
||||||
|
|
||||||
var p,q,d:float
|
|
||||||
|
|
||||||
p=b/(2.0*a)
|
|
||||||
|
|
||||||
if p==Inf or p==NegInf: #linear equation..
|
|
||||||
var linrt= -c/b
|
|
||||||
if linrt==Inf or linrt==NegInf: #constant only
|
|
||||||
return @[]
|
|
||||||
return @[linrt]
|
|
||||||
|
|
||||||
q=c/a
|
|
||||||
d=p*p-q
|
|
||||||
|
|
||||||
if d<0.0:
|
|
||||||
#check for inside zerotol range for neg. roots
|
|
||||||
var err=a*p*p-b*p+c #evaluate error at parabola center axis
|
|
||||||
if(err<=zerotol): return @[-p]
|
|
||||||
return @[]
|
|
||||||
else:
|
|
||||||
var sr=sqrt(d)
|
|
||||||
result= @[-sr-p,sr-p]
|
|
||||||
|
|
||||||
proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
|
|
||||||
## helper function for `roots` function
|
|
||||||
## quickly computes a range, guaranteed to contain
|
|
||||||
## all the real roots of the polynomial
|
|
||||||
# see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
|
|
||||||
|
|
||||||
var deg=p.degree
|
|
||||||
var d=p[deg]
|
|
||||||
var bound1,bound2:float
|
|
||||||
|
|
||||||
for i in countup(0,deg):
|
|
||||||
var c=abs(p.cofs[i]/d)
|
|
||||||
bound1=max(bound1,c+1.0)
|
|
||||||
bound2=bound2+c
|
|
||||||
|
|
||||||
bound2=max(1.0,bound2)
|
|
||||||
result.xmax=min(bound1,bound2)
|
|
||||||
result.xmin= -result.xmax
|
|
||||||
|
|
||||||
|
|
||||||
proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
|
|
||||||
## helper function for `roots` function
|
|
||||||
## try to do a numeric search for a single root in range xp0-xp1,
|
|
||||||
## adding it to `res` (allocating `res` if nil)
|
|
||||||
var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
|
|
||||||
if br.success:
|
|
||||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
|
||||||
res.add(br.rootx)
|
|
||||||
else:
|
|
||||||
#this might be a 'touching' case, check function value against
|
|
||||||
#zero tolerance
|
|
||||||
if abs(br.rooty)<=zerotol:
|
|
||||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
|
||||||
res.add(br.rootx)
|
|
||||||
|
|
||||||
|
|
||||||
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
|
|
||||||
## Computes the real roots of the polynomial `p`
|
|
||||||
## `tol` is the tolerance used to break searching for each root when reached.
|
|
||||||
## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
|
|
||||||
## and is used to find roots for curves that only 'touch' the x-axis.
|
|
||||||
## `mergetol` is the tolerance, of which two x-values are considered being the same root.
|
|
||||||
## `maxiter` can be used to limit the number of iterations for each root.
|
|
||||||
## Returns a (possibly empty) sorted sequence with the solutions.
|
|
||||||
var deg=p.degree
|
|
||||||
if deg<=0: #constant only => no roots
|
|
||||||
return @[]
|
|
||||||
elif p.degree==1: #linear
|
|
||||||
var linrt= -p.cofs[0]/p.cofs[1]
|
|
||||||
if linrt==Inf or linrt==NegInf:
|
|
||||||
return @[] #constant only => no roots
|
|
||||||
return @[linrt]
|
|
||||||
elif p.degree==2:
|
|
||||||
return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
|
|
||||||
else:
|
|
||||||
# degree >=3 , find min/max points of polynomial with recursive
|
|
||||||
# derivative and do a numerical search for root between each min/max
|
|
||||||
var range=p.getRangeForRoots()
|
|
||||||
var minmax=p.derivative.roots(tol,zerotol,mergetol)
|
|
||||||
result= @[]
|
|
||||||
if minmax!=nil: #ie. we have minimas/maximas in this function
|
|
||||||
for x in minmax.items:
|
|
||||||
addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
|
|
||||||
range.xmin=x
|
|
||||||
addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)
|
|
||||||
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2011 Philippe Lhoste
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Module for converting an integer to a Roman numeral.
|
|
||||||
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
|
||||||
##
|
|
||||||
## **Warning:** This module will be moved out of the stdlib and into a
|
|
||||||
## Nimble package, don't use it.
|
|
||||||
|
|
||||||
const
|
|
||||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
|
||||||
'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
|
|
||||||
|
|
||||||
proc romanToDecimal*(romanVal: string): int =
|
|
||||||
## Converts a Roman numeral to its int representation.
|
|
||||||
result = 0
|
|
||||||
var prevVal = 0
|
|
||||||
for i in countdown(romanVal.len - 1, 0):
|
|
||||||
var val = 0
|
|
||||||
case romanVal[i]
|
|
||||||
of 'I', 'i': val = 1
|
|
||||||
of 'V', 'v': val = 5
|
|
||||||
of 'X', 'x': val = 10
|
|
||||||
of 'L', 'l': val = 50
|
|
||||||
of 'C', 'c': val = 100
|
|
||||||
of 'D', 'd': val = 500
|
|
||||||
of 'M', 'm': val = 1000
|
|
||||||
else:
|
|
||||||
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
|
|
||||||
if val >= prevVal:
|
|
||||||
inc(result, val)
|
|
||||||
else:
|
|
||||||
dec(result, val)
|
|
||||||
prevVal = val
|
|
||||||
|
|
||||||
proc decimalToRoman*(number: range[1..3_999]): string =
|
|
||||||
## Converts a number to a Roman numeral.
|
|
||||||
const romanComposites = [
|
|
||||||
("M", 1000), ("CM", 900),
|
|
||||||
("D", 500), ("CD", 400), ("C", 100),
|
|
||||||
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
|
|
||||||
("V", 5), ("IV", 4), ("I", 1)]
|
|
||||||
result = ""
|
|
||||||
var decVal: int = number
|
|
||||||
for key, val in items(romanComposites):
|
|
||||||
while decVal >= val:
|
|
||||||
dec(decVal, val)
|
|
||||||
result.add(key)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
for i in 1 .. 3_999:
|
|
||||||
assert i == i.decimalToRoman.romanToDecimal
|
|
||||||
|
|
||||||
|
|
@ -1927,7 +1927,7 @@ type
|
||||||
{.deprecated: [TFloatFormat: FloatFormatMode].}
|
{.deprecated: [TFloatFormat: FloatFormatMode].}
|
||||||
|
|
||||||
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16;
|
precision: range[-1..32] = 16;
|
||||||
decimalSep = '.'): string {.
|
decimalSep = '.'): string {.
|
||||||
noSideEffect, rtl, extern: "nsu$1".} =
|
noSideEffect, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
|
|
@ -1939,7 +1939,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
## `precision`'s default value is the maximum number of meaningful digits
|
## `precision`'s default value is the maximum number of meaningful digits
|
||||||
## after the decimal point for Nim's ``biggestFloat`` type.
|
## after the decimal point for Nim's ``biggestFloat`` type.
|
||||||
##
|
##
|
||||||
## If ``precision == 0``, it tries to format it nicely.
|
## If ``precision == -1``, it tries to format it nicely.
|
||||||
when defined(js):
|
when defined(js):
|
||||||
var res: cstring
|
var res: cstring
|
||||||
case format
|
case format
|
||||||
|
|
@ -1961,7 +1961,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
buf {.noinit.}: array[0..2500, char]
|
buf {.noinit.}: array[0..2500, char]
|
||||||
L: cint
|
L: cint
|
||||||
frmtstr[0] = '%'
|
frmtstr[0] = '%'
|
||||||
if precision > 0:
|
if precision >= 0:
|
||||||
frmtstr[1] = '#'
|
frmtstr[1] = '#'
|
||||||
frmtstr[2] = '.'
|
frmtstr[2] = '.'
|
||||||
frmtstr[3] = '*'
|
frmtstr[3] = '*'
|
||||||
|
|
@ -1984,7 +1984,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
# but nothing else is possible:
|
# but nothing else is possible:
|
||||||
if buf[i] in {'.', ','}: result[i] = decimalsep
|
if buf[i] in {'.', ','}: result[i] = decimalsep
|
||||||
else: result[i] = buf[i]
|
else: result[i] = buf[i]
|
||||||
when defined(vcc):
|
when defined(windows):
|
||||||
# VS pre 2015 violates the C standard: "The exponent always contains at
|
# VS pre 2015 violates the C standard: "The exponent always contains at
|
||||||
# least two digits, and only as many more digits as necessary to
|
# least two digits, and only as many more digits as necessary to
|
||||||
# represent the exponent." [C11 §7.21.6.1]
|
# represent the exponent." [C11 §7.21.6.1]
|
||||||
|
|
@ -1995,7 +1995,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
result.setLen(result.len - 1)
|
result.setLen(result.len - 1)
|
||||||
|
|
||||||
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16; decimalSep = '.'): string {.
|
precision: range[-1..32] = 16; decimalSep = '.'): string {.
|
||||||
noSideEffect, rtl, extern: "nsu$1".} =
|
noSideEffect, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
##
|
##
|
||||||
|
|
@ -2006,16 +2006,16 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
## `precision`'s default value is the maximum number of meaningful digits
|
## `precision`'s default value is the maximum number of meaningful digits
|
||||||
## after the decimal point for Nim's ``float`` type.
|
## after the decimal point for Nim's ``float`` type.
|
||||||
##
|
##
|
||||||
## If ``precision == 0``, it tries to format it nicely.
|
## If ``precision == -1``, it tries to format it nicely.
|
||||||
##
|
##
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## let x = 123.456
|
## let x = 123.456
|
||||||
## echo x.formatFloat() # 123.4560000000000
|
## doAssert x.formatFloat() == "123.4560000000000"
|
||||||
## echo x.formatFloat(ffDecimal, 4) # 123.4560
|
## doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
|
||||||
## echo x.formatFloat(ffScientific, 2) # 1.23e+02
|
## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
|
||||||
##
|
##
|
||||||
result = formatBiggestFloat(f, format, precision, decimalSep)
|
result = formatBiggestFloat(f, format, precision, decimalSep)
|
||||||
|
|
||||||
|
|
@ -2471,12 +2471,14 @@ when isMainModule:
|
||||||
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
|
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
|
||||||
doAssert wordWrap(inp, 10, false) == outp
|
doAssert wordWrap(inp, 10, false) == outp
|
||||||
|
|
||||||
|
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
|
||||||
|
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
|
||||||
|
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
|
||||||
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
|
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
|
||||||
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
|
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
|
||||||
["1,0e-11", "1,0e-011"]
|
["1,0e-11", "1,0e-011"]
|
||||||
# bug #6589
|
# bug #6589
|
||||||
doAssert formatFloat(123.456, ffScientific, precision=0) in
|
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
|
||||||
["1.234560e+02", "1.234560e+002"]
|
|
||||||
|
|
||||||
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
||||||
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
|
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
|
||||||
##
|
##
|
||||||
## proc `$`*(T: typedesc): string = name(T)
|
## proc `$`*(T: typedesc): string = name(T)
|
||||||
##
|
##
|
||||||
## template test(x): stmt =
|
## template test(x): typed =
|
||||||
## echo "type: ", type(x), ", value: ", x
|
## echo "type: ", type(x), ", value: ", x
|
||||||
##
|
##
|
||||||
## test 42
|
## test 42
|
||||||
|
|
|
||||||
|
|
@ -308,11 +308,16 @@ proc `$`*(u: Uri): string =
|
||||||
result.add(":")
|
result.add(":")
|
||||||
result.add(u.password)
|
result.add(u.password)
|
||||||
result.add("@")
|
result.add("@")
|
||||||
result.add(u.hostname)
|
if u.hostname.endswith('/'):
|
||||||
|
result.add(u.hostname[0..^2])
|
||||||
|
else:
|
||||||
|
result.add(u.hostname)
|
||||||
if u.port.len > 0:
|
if u.port.len > 0:
|
||||||
result.add(":")
|
result.add(":")
|
||||||
result.add(u.port)
|
result.add(u.port)
|
||||||
if u.path.len > 0:
|
if u.path.len > 0:
|
||||||
|
if u.hostname.len > 0 and u.path[0] != '/':
|
||||||
|
result.add('/')
|
||||||
result.add(u.path)
|
result.add(u.path)
|
||||||
if u.query.len > 0:
|
if u.query.len > 0:
|
||||||
result.add("?")
|
result.add("?")
|
||||||
|
|
@ -483,6 +488,34 @@ when isMainModule:
|
||||||
let foo = parseUri("http://localhost:9515") / "status"
|
let foo = parseUri("http://localhost:9515") / "status"
|
||||||
doAssert $foo == "http://localhost:9515/status"
|
doAssert $foo == "http://localhost:9515/status"
|
||||||
|
|
||||||
|
# bug #6649 #6652
|
||||||
|
block:
|
||||||
|
var foo = parseUri("http://example.com")
|
||||||
|
foo.hostname = "example.com"
|
||||||
|
foo.path = "baz"
|
||||||
|
doAssert $foo == "http://example.com/baz"
|
||||||
|
|
||||||
|
foo.hostname = "example.com/"
|
||||||
|
foo.path = "baz"
|
||||||
|
doAssert $foo == "http://example.com/baz"
|
||||||
|
|
||||||
|
foo.hostname = "example.com"
|
||||||
|
foo.path = "/baz"
|
||||||
|
doAssert $foo == "http://example.com/baz"
|
||||||
|
|
||||||
|
foo.hostname = "example.com/"
|
||||||
|
foo.path = "/baz"
|
||||||
|
doAssert $foo == "http://example.com/baz"
|
||||||
|
|
||||||
|
foo.hostname = "example.com/"
|
||||||
|
foo.port = "8000"
|
||||||
|
foo.path = "baz"
|
||||||
|
doAssert $foo == "http://example.com:8000/baz"
|
||||||
|
|
||||||
|
foo = parseUri("file:/dir/file")
|
||||||
|
foo.path = "relative"
|
||||||
|
doAssert $foo == "file:relative"
|
||||||
|
|
||||||
# isAbsolute tests
|
# isAbsolute tests
|
||||||
block:
|
block:
|
||||||
doAssert "www.google.com".parseUri().isAbsolute() == false
|
doAssert "www.google.com".parseUri().isAbsolute() == false
|
||||||
|
|
@ -524,4 +557,4 @@ when isMainModule:
|
||||||
doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
|
doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
|
||||||
doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
|
doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
|
||||||
|
|
||||||
echo("All good!")
|
echo("All good!")
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,13 @@ type
|
||||||
WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation.
|
WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation.
|
||||||
ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation.
|
ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation.
|
||||||
|
|
||||||
|
StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
|
||||||
|
## to them. A StackTraceEntry is a single entry of the
|
||||||
|
## stack trace.
|
||||||
|
procname*: cstring ## name of the proc that is currently executing
|
||||||
|
line*: int ## line number of the proc that is currently executing
|
||||||
|
filename*: cstring ## filename of the proc that is currently executing
|
||||||
|
|
||||||
Exception* {.compilerproc.} = object of RootObj ## \
|
Exception* {.compilerproc.} = object of RootObj ## \
|
||||||
## Base exception class.
|
## Base exception class.
|
||||||
##
|
##
|
||||||
|
|
@ -468,7 +475,10 @@ type
|
||||||
msg* {.exportc: "message".}: string ## the exception's message. Not
|
msg* {.exportc: "message".}: string ## the exception's message. Not
|
||||||
## providing an exception message
|
## providing an exception message
|
||||||
## is bad style.
|
## is bad style.
|
||||||
trace: string
|
when defined(js):
|
||||||
|
trace: string
|
||||||
|
else:
|
||||||
|
trace: seq[StackTraceEntry]
|
||||||
up: ref Exception # used for stacking exceptions. Not exported!
|
up: ref Exception # used for stacking exceptions. Not exported!
|
||||||
|
|
||||||
SystemError* = object of Exception ## \
|
SystemError* = object of Exception ## \
|
||||||
|
|
@ -895,7 +905,7 @@ proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
|
||||||
proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
|
proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
|
||||||
proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
|
proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
|
||||||
## computes the integer division. This is roughly the same as
|
## computes the integer division. This is roughly the same as
|
||||||
## ``floor(x/y)``.
|
## ``trunc(x/y)``.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## 1 div 2 == 0
|
## 1 div 2 == 0
|
||||||
|
|
@ -1097,7 +1107,7 @@ proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
|
||||||
|
|
||||||
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
|
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
|
||||||
## computes the integer division. This is roughly the same as
|
## computes the integer division. This is roughly the same as
|
||||||
## ``floor(x/y)``.
|
## ``trunc(x/y)``.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## (7 div 5) == 1
|
## (7 div 5) == 1
|
||||||
|
|
@ -2015,19 +2025,19 @@ when defined(nimNewRoof):
|
||||||
yield res
|
yield res
|
||||||
inc(res)
|
inc(res)
|
||||||
|
|
||||||
iterator `..`*(a, b: int64): int64 {.inline.} =
|
template dotdotImpl(t) {.dirty.} =
|
||||||
## A special version of ``..`` for ``int64`` only.
|
iterator `..`*(a, b: t): t {.inline.} =
|
||||||
var res = a
|
## A type specialized version of ``..`` for convenience so that
|
||||||
while res <= b:
|
## mixing integer types work better.
|
||||||
yield res
|
var res = a
|
||||||
inc(res)
|
while res <= b:
|
||||||
|
yield res
|
||||||
|
inc(res)
|
||||||
|
|
||||||
iterator `..`*(a, b: int32): int32 {.inline.} =
|
dotdotImpl(int64)
|
||||||
## A special version of ``..`` for ``int32`` only.
|
dotdotImpl(int32)
|
||||||
var res = a
|
dotdotImpl(uint64)
|
||||||
while res <= b:
|
dotdotImpl(uint32)
|
||||||
yield res
|
|
||||||
inc(res)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
|
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
|
||||||
|
|
@ -2763,9 +2773,9 @@ when defined(nimvarargstyped):
|
||||||
## pretends to be free of side effects, so that it can be used for debugging
|
## pretends to be free of side effects, so that it can be used for debugging
|
||||||
## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
|
## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
|
||||||
else:
|
else:
|
||||||
proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
|
proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
|
||||||
benign, sideEffect.}
|
benign, sideEffect.}
|
||||||
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
|
proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
|
||||||
tags: [], raises: [].}
|
tags: [], raises: [].}
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string;
|
template newException*(exceptn: typedesc, message: string;
|
||||||
|
|
@ -3706,7 +3716,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## import strutils
|
## import strutils
|
||||||
##
|
##
|
||||||
## template testException(exception, code: expr): stmt =
|
## template testException(exception, code: untyped): typed =
|
||||||
## try:
|
## try:
|
||||||
## let pos = instantiationInfo()
|
## let pos = instantiationInfo()
|
||||||
## discard(code)
|
## discard(code)
|
||||||
|
|
@ -3844,11 +3854,11 @@ type
|
||||||
{.deprecated: [PNimrodNode: NimNode].}
|
{.deprecated: [PNimrodNode: NimNode].}
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
template eval*(blk: stmt): stmt =
|
template eval*(blk: typed): typed =
|
||||||
## executes a block of code at compile time just as if it was a macro
|
## executes a block of code at compile time just as if it was a macro
|
||||||
## optionally, the block can return an AST tree that will replace the
|
## optionally, the block can return an AST tree that will replace the
|
||||||
## eval expression
|
## eval expression
|
||||||
macro payload: stmt {.gensym.} = blk
|
macro payload: typed {.gensym.} = blk
|
||||||
payload()
|
payload()
|
||||||
|
|
||||||
when hasAlloc:
|
when hasAlloc:
|
||||||
|
|
|
||||||
|
|
@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
|
||||||
result = memLoc
|
result = memLoc
|
||||||
|
|
||||||
when defined(vcc):
|
when defined(vcc):
|
||||||
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
|
when defined(cpp):
|
||||||
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
|
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
|
||||||
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
|
{.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
|
||||||
{.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
|
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
|
||||||
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
|
{.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
|
||||||
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
|
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
|
||||||
|
{.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
|
||||||
|
else:
|
||||||
|
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
|
||||||
|
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
|
||||||
|
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
|
||||||
|
{.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
|
||||||
|
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
|
||||||
|
{.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
|
||||||
|
|
||||||
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
|
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
|
||||||
when sizeof(T) == 8:
|
when sizeof(T) == 8:
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ proc commandPrompt() =
|
||||||
if dbgUser.len == 0: dbgUser.len = oldLen
|
if dbgUser.len == 0: dbgUser.len = oldLen
|
||||||
# now look what we have to do:
|
# now look what we have to do:
|
||||||
var i = scanWord(addr dbgUser.data, dbgTemp, 0)
|
var i = scanWord(addr dbgUser.data, dbgTemp, 0)
|
||||||
template `?`(x: expr): expr = dbgTemp == cstring(x)
|
template `?`(x: untyped): untyped = dbgTemp == cstring(x)
|
||||||
if ?"s" or ?"step":
|
if ?"s" or ?"step":
|
||||||
dbgState = dbStepInto
|
dbgState = dbStepInto
|
||||||
again = false
|
again = false
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,52 @@ when not hasThreadSupport:
|
||||||
var
|
var
|
||||||
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
|
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
|
||||||
|
|
||||||
|
const
|
||||||
|
reraisedFromBegin = -10
|
||||||
|
reraisedFromEnd = -100
|
||||||
|
|
||||||
|
template reraisedFrom(z): untyped =
|
||||||
|
StackTraceEntry(procname: nil, line: z, filename: nil)
|
||||||
|
|
||||||
|
proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
|
||||||
|
var
|
||||||
|
it = f
|
||||||
|
i = 0
|
||||||
|
while it != nil:
|
||||||
|
inc(i)
|
||||||
|
it = it.prev
|
||||||
|
var last = i-1
|
||||||
|
if s.isNil:
|
||||||
|
s = newSeq[StackTraceEntry](i)
|
||||||
|
else:
|
||||||
|
last = s.len + i - 1
|
||||||
|
s.setLen(last+1)
|
||||||
|
it = f
|
||||||
|
while it != nil:
|
||||||
|
s[last] = StackTraceEntry(procname: it.procname,
|
||||||
|
line: it.line,
|
||||||
|
filename: it.filename)
|
||||||
|
it = it.prev
|
||||||
|
dec last
|
||||||
|
|
||||||
|
template addFrameEntry(s, f: untyped) =
|
||||||
|
var oldLen = s.len
|
||||||
|
add(s, f.filename)
|
||||||
|
if f.line > 0:
|
||||||
|
add(s, '(')
|
||||||
|
add(s, $f.line)
|
||||||
|
add(s, ')')
|
||||||
|
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
||||||
|
add(s, f.procname)
|
||||||
|
add(s, "\n")
|
||||||
|
|
||||||
|
proc `$`(s: seq[StackTraceEntry]): string =
|
||||||
|
result = newStringOfCap(2000)
|
||||||
|
for i in 0 .. s.len-1:
|
||||||
|
if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
|
||||||
|
elif s[i].line == reraisedFromEnd: result.add "]]\n"
|
||||||
|
else: addFrameEntry(result, s[i])
|
||||||
|
|
||||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
var
|
var
|
||||||
|
|
@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
if tempFrames[j] == nil:
|
if tempFrames[j] == nil:
|
||||||
add(s, "(")
|
add(s, "(")
|
||||||
add(s, $skipped)
|
add(s, $skipped)
|
||||||
add(s, " calls omitted) ...")
|
add(s, " calls omitted) ...\n")
|
||||||
else:
|
else:
|
||||||
var oldLen = s.len
|
addFrameEntry(s, tempFrames[j])
|
||||||
add(s, tempFrames[j].filename)
|
|
||||||
if tempFrames[j].line > 0:
|
|
||||||
add(s, '(')
|
|
||||||
add(s, $tempFrames[j].line)
|
|
||||||
add(s, ')')
|
|
||||||
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
|
||||||
add(s, tempFrames[j].procname)
|
|
||||||
add(s, "\n")
|
|
||||||
|
|
||||||
proc stackTraceAvailable*(): bool
|
proc stackTraceAvailable*(): bool
|
||||||
|
|
||||||
|
|
@ -221,6 +259,13 @@ when hasSomeStackTrace:
|
||||||
auxWriteStackTraceWithBacktrace(s)
|
auxWriteStackTraceWithBacktrace(s)
|
||||||
else:
|
else:
|
||||||
add(s, "No stack traceback available\n")
|
add(s, "No stack traceback available\n")
|
||||||
|
|
||||||
|
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
|
||||||
|
when NimStackTrace:
|
||||||
|
auxWriteStackTrace(framePtr, s)
|
||||||
|
else:
|
||||||
|
s = nil
|
||||||
|
|
||||||
proc stackTraceAvailable(): bool =
|
proc stackTraceAvailable(): bool =
|
||||||
when NimStackTrace:
|
when NimStackTrace:
|
||||||
if framePtr == nil:
|
if framePtr == nil:
|
||||||
|
|
@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} =
|
||||||
else:
|
else:
|
||||||
endbStep() # call the debugger
|
endbStep() # call the debugger
|
||||||
|
|
||||||
when false:
|
|
||||||
proc rawRaise*(e: ref Exception) =
|
|
||||||
## undocumented. Do not use.
|
|
||||||
pushCurrentException(e)
|
|
||||||
c_longjmp(excHandler.context, 1)
|
|
||||||
|
|
||||||
var onUnhandledException*: (proc (errorMsg: string) {.
|
var onUnhandledException*: (proc (errorMsg: string) {.
|
||||||
nimcall.}) ## set this error \
|
nimcall.}) ## set this error \
|
||||||
## handler to override the existing behaviour on an unhandled exception.
|
## handler to override the existing behaviour on an unhandled exception.
|
||||||
|
|
@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
var buf = newStringOfCap(2000)
|
var buf = newStringOfCap(2000)
|
||||||
if isNil(e.trace): rawWriteStackTrace(buf)
|
if isNil(e.trace): rawWriteStackTrace(buf)
|
||||||
else: add(buf, e.trace)
|
else: add(buf, $e.trace)
|
||||||
add(buf, "Error: unhandled exception: ")
|
add(buf, "Error: unhandled exception: ")
|
||||||
if not isNil(e.msg): add(buf, e.msg)
|
if not isNil(e.msg): add(buf, e.msg)
|
||||||
add(buf, " [")
|
add(buf, " [")
|
||||||
|
|
@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
|
||||||
if e.name.isNil: e.name = ename
|
if e.name.isNil: e.name = ename
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
if e.trace.isNil:
|
if e.trace.isNil:
|
||||||
e.trace = ""
|
|
||||||
rawWriteStackTrace(e.trace)
|
rawWriteStackTrace(e.trace)
|
||||||
elif framePtr != nil:
|
elif framePtr != nil:
|
||||||
e.trace.add "[[reraised from:\n"
|
e.trace.add reraisedFrom(reraisedFromBegin)
|
||||||
auxWriteStackTrace(framePtr, e.trace)
|
auxWriteStackTrace(framePtr, e.trace)
|
||||||
e.trace.add "]]\n"
|
e.trace.add reraisedFrom(reraisedFromEnd)
|
||||||
raiseExceptionAux(e)
|
raiseExceptionAux(e)
|
||||||
|
|
||||||
proc reraiseException() {.compilerRtl.} =
|
proc reraiseException() {.compilerRtl.} =
|
||||||
|
|
@ -349,10 +387,15 @@ proc getStackTrace(): string =
|
||||||
|
|
||||||
proc getStackTrace(e: ref Exception): string =
|
proc getStackTrace(e: ref Exception): string =
|
||||||
if not isNil(e) and not isNil(e.trace):
|
if not isNil(e) and not isNil(e.trace):
|
||||||
result = e.trace
|
result = $e.trace
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
|
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
|
||||||
|
## Returns the attached stack trace to the exception ``e`` as
|
||||||
|
## a ``seq``. This is not yet available for the JS backend.
|
||||||
|
shallowCopy(result, e.trace)
|
||||||
|
|
||||||
when defined(nimRequiresNimFrame):
|
when defined(nimRequiresNimFrame):
|
||||||
proc stackOverflow() {.noinline.} =
|
proc stackOverflow() {.noinline.} =
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||||
|
|
||||||
template color(c): expr = c.refCount and colorMask
|
template color(c): untyped = c.refCount and colorMask
|
||||||
template setColor(c, col) =
|
template setColor(c, col) =
|
||||||
c.refcount = c.refcount and not colorMask or col
|
c.refcount = c.refcount and not colorMask or col
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
|
||||||
## it is not necessary to call this directly; use 'spawnX' instead.
|
## it is not necessary to call this directly; use 'spawnX' instead.
|
||||||
result = gSomeReady.event
|
result = gSomeReady.event
|
||||||
|
|
||||||
proc spawn*(call: stmt) {.magic: "Spawn".}
|
proc spawn*(call: typed) {.magic: "Spawn".}
|
||||||
## always spawns a new task, so that the 'call' is never executed on
|
## always spawns a new task, so that the 'call' is never executed on
|
||||||
## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
|
## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
|
||||||
## is gcsafe and has 'void' as the return type.
|
## is gcsafe and has 'void' as the return type.
|
||||||
|
|
||||||
template spawnX*(call: stmt) =
|
template spawnX*(call: typed) =
|
||||||
## spawns a new task if a CPU core is ready, otherwise executes the
|
## spawns a new task if a CPU core is ready, otherwise executes the
|
||||||
## call in the calling thread. Usually it is advised to
|
## call in the calling thread. Usually it is advised to
|
||||||
## use 'spawn' in order to not block the producer for an unknown
|
## use 'spawn' in order to not block the producer for an unknown
|
||||||
|
|
|
||||||
|
|
@ -322,37 +322,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||||
result.add x
|
result.add x
|
||||||
|
|
||||||
proc add*(result: var string; x: float) =
|
proc add*(result: var string; x: float) =
|
||||||
var buf: array[0..64, char]
|
when nimvm:
|
||||||
when defined(nimNoArrayToCstringConversion):
|
result.add $x
|
||||||
var n: int = c_sprintf(addr buf, "%.16g", x)
|
|
||||||
else:
|
else:
|
||||||
var n: int = c_sprintf(buf, "%.16g", x)
|
var buf: array[0..64, char]
|
||||||
var hasDot = false
|
when defined(nimNoArrayToCstringConversion):
|
||||||
for i in 0..n-1:
|
var n: int = c_sprintf(addr buf, "%.16g", x)
|
||||||
if buf[i] == ',':
|
|
||||||
buf[i] = '.'
|
|
||||||
hasDot = true
|
|
||||||
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
|
|
||||||
hasDot = true
|
|
||||||
if not hasDot:
|
|
||||||
buf[n] = '.'
|
|
||||||
buf[n+1] = '0'
|
|
||||||
buf[n+2] = '\0'
|
|
||||||
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
|
|
||||||
# of '-1.#IND' are produced.
|
|
||||||
# We want to get rid of these here:
|
|
||||||
if buf[n-1] in {'n', 'N', 'D', 'd'}:
|
|
||||||
result.add "nan"
|
|
||||||
elif buf[n-1] == 'F':
|
|
||||||
if buf[0] == '-':
|
|
||||||
result.add "-inf"
|
|
||||||
else:
|
else:
|
||||||
result.add "inf"
|
var n: int = c_sprintf(buf, "%.16g", x)
|
||||||
else:
|
var hasDot = false
|
||||||
var i = 0
|
for i in 0..n-1:
|
||||||
while buf[i] != '\0':
|
if buf[i] == ',':
|
||||||
result.add buf[i]
|
buf[i] = '.'
|
||||||
inc i
|
hasDot = true
|
||||||
|
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
|
||||||
|
hasDot = true
|
||||||
|
if not hasDot:
|
||||||
|
buf[n] = '.'
|
||||||
|
buf[n+1] = '0'
|
||||||
|
buf[n+2] = '\0'
|
||||||
|
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
|
||||||
|
# of '-1.#IND' are produced.
|
||||||
|
# We want to get rid of these here:
|
||||||
|
if buf[n-1] in {'n', 'N', 'D', 'd'}:
|
||||||
|
result.add "nan"
|
||||||
|
elif buf[n-1] == 'F':
|
||||||
|
if buf[0] == '-':
|
||||||
|
result.add "-inf"
|
||||||
|
else:
|
||||||
|
result.add "inf"
|
||||||
|
else:
|
||||||
|
var i = 0
|
||||||
|
while buf[i] != '\0':
|
||||||
|
result.add buf[i]
|
||||||
|
inc i
|
||||||
|
|
||||||
proc nimFloatToStr(f: float): string {.compilerproc.} =
|
proc nimFloatToStr(f: float): string {.compilerproc.} =
|
||||||
result = newStringOfCap(8)
|
result = newStringOfCap(8)
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,7 @@ proc testVarargs(x, y, z: int): seq[int] =
|
||||||
|
|
||||||
result = waitFor all(a, b, c)
|
result = waitFor all(a, b, c)
|
||||||
|
|
||||||
suite "tasyncall":
|
block:
|
||||||
test "testFuturesWithValue":
|
|
||||||
let
|
let
|
||||||
startTime = cpuTime()
|
startTime = cpuTime()
|
||||||
results = testFuturesWithValue(42)
|
results = testFuturesWithValue(42)
|
||||||
|
|
@ -51,14 +50,14 @@ suite "tasyncall":
|
||||||
doAssert execTime * 1000 < taskCount * sleepDuration
|
doAssert execTime * 1000 < taskCount * sleepDuration
|
||||||
doAssert results == expected
|
doAssert results == expected
|
||||||
|
|
||||||
test "testFuturesWithoutValues":
|
block:
|
||||||
let startTime = cpuTime()
|
let startTime = cpuTime()
|
||||||
testFuturesWithoutValues()
|
testFuturesWithoutValues()
|
||||||
let execTime = cpuTime() - startTime
|
let execTime = cpuTime() - startTime
|
||||||
|
|
||||||
doAssert execTime * 1000 < taskCount * sleepDuration
|
doAssert execTime * 1000 < taskCount * sleepDuration
|
||||||
|
|
||||||
test "testVarargs":
|
block:
|
||||||
let
|
let
|
||||||
startTime = cpuTime()
|
startTime = cpuTime()
|
||||||
results = testVarargs(1, 2, 3)
|
results = testVarargs(1, 2, 3)
|
||||||
|
|
@ -68,7 +67,7 @@ suite "tasyncall":
|
||||||
doAssert execTime * 100 < taskCount * sleepDuration
|
doAssert execTime * 100 < taskCount * sleepDuration
|
||||||
doAssert results == expected
|
doAssert results == expected
|
||||||
|
|
||||||
test "all on seq[Future]":
|
block:
|
||||||
let
|
let
|
||||||
noIntFuturesFut = all(newSeq[Future[int]]())
|
noIntFuturesFut = all(newSeq[Future[int]]())
|
||||||
noVoidFuturesFut = all(newSeq[Future[void]]())
|
noVoidFuturesFut = all(newSeq[Future[void]]())
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,22 @@ discard """
|
||||||
output: "Finished"
|
output: "Finished"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncdispatch
|
import asyncdispatch, asyncnet
|
||||||
|
|
||||||
|
proc createServer(port: Port) {.async.} =
|
||||||
|
var server = newAsyncSocket()
|
||||||
|
server.setSockOpt(OptReuseAddr, true)
|
||||||
|
bindAddr(server, port)
|
||||||
|
server.listen()
|
||||||
|
while true:
|
||||||
|
let client = await server.accept()
|
||||||
|
discard await client.recvLine()
|
||||||
|
|
||||||
|
asyncCheck createServer(10335.Port)
|
||||||
|
|
||||||
proc f(): Future[void] {.async.} =
|
proc f(): Future[void] {.async.} =
|
||||||
let s = newAsyncNativeSocket()
|
let s = newAsyncNativeSocket()
|
||||||
await s.connect("example.com", 80.Port)
|
await s.connect("localhost", 10335.Port)
|
||||||
await s.send("123")
|
await s.send("123")
|
||||||
echo "Finished"
|
echo "Finished"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ proc readMessages(server: AsyncFD) {.async.} =
|
||||||
size = 0
|
size = 0
|
||||||
var grammString = $cstring(addr buffer)
|
var grammString = $cstring(addr buffer)
|
||||||
if grammString.startswith("Message ") and
|
if grammString.startswith("Message ") and
|
||||||
saddr.sin_addr.s_addr == 0x100007F:
|
saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32):
|
||||||
await sendTo(server, addr grammString[0], len(grammString),
|
await sendTo(server, addr grammString[0], len(grammString),
|
||||||
cast[ptr SockAddr](addr saddr), slen)
|
cast[ptr SockAddr](addr saddr), slen)
|
||||||
inc(msgCount)
|
inc(msgCount)
|
||||||
|
|
|
||||||
33
tests/ccgbugs/trefseqsort.nim
Normal file
33
tests/ccgbugs/trefseqsort.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
discard """
|
||||||
|
output: '''@[0, 4, 9, 1, 3, 2]
|
||||||
|
@[0, 1, 2, 3, 9]'''
|
||||||
|
"""
|
||||||
|
# bug #6724
|
||||||
|
import algorithm
|
||||||
|
|
||||||
|
type
|
||||||
|
Bar = object
|
||||||
|
bar: ref seq[int]
|
||||||
|
Foo = ref Bar
|
||||||
|
|
||||||
|
proc test(x: ref Foo) =
|
||||||
|
x.bar[].del(1)
|
||||||
|
x.bar[].sort(cmp)
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var foo: ref Foo
|
||||||
|
new(foo)
|
||||||
|
|
||||||
|
var s = @[0, 4, 9, 1, 3, 2]
|
||||||
|
|
||||||
|
var sr: ref seq[int]
|
||||||
|
new(sr)
|
||||||
|
sr[] = s
|
||||||
|
|
||||||
|
foo[] = Foo(bar: sr)
|
||||||
|
echo($foo.bar[])
|
||||||
|
|
||||||
|
test(foo)
|
||||||
|
echo($foo.bar[])
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: "hello"
|
output: "hello"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
10
tests/cpp/tcasts.nim
Normal file
10
tests/cpp/tcasts.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim cpp $file"
|
||||||
|
output: ""
|
||||||
|
"""
|
||||||
|
|
||||||
|
block: #5979
|
||||||
|
var a = 'a'
|
||||||
|
var p: pointer = cast[pointer](a)
|
||||||
|
var c = cast[char](p)
|
||||||
|
doAssert(c == 'a')
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: '''
|
output: '''
|
||||||
cat
|
cat
|
||||||
cat
|
cat
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: '''foo
|
output: '''foo
|
||||||
bar
|
bar
|
||||||
Need odd and >= 3 digits##
|
Need odd and >= 3 digits##
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: ''''''
|
output: ''''''
|
||||||
disabled: true
|
disabled: true
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
cmd: "nim cpp --threads:on $file"
|
cmd: "nim cpp --threads:on $file"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: '''6.0'''
|
output: '''6.0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: '''int
|
output: '''int
|
||||||
float'''
|
float'''
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: '''Hello world'''
|
output: '''Hello world'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nativesockets
|
import nativesockets
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
|
"""
|
||||||
|
|
||||||
# bug #2259
|
# bug #2259
|
||||||
type Mat4f* = array[0..15, float]
|
type Mat4f* = array[0..15, float]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import tables, lists
|
import tables, lists
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
output: "42"
|
output: "42"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
|
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
cmd: "nim cpp --hints:on --threads:on $options $file"
|
cmd: "nim cpp --hints:on --threads:on $options $file"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
output: '''100'''
|
output: '''100'''
|
||||||
cmd: "nim cpp $file"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import typeinfo
|
import typeinfo
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
# bug #2841
|
# bug #2841
|
||||||
import typeinfo
|
import typeinfo
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim cpp $file"
|
targets: "cpp"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
|
targets: "cpp"
|
||||||
output: '''(x: 1.0)
|
output: '''(x: 1.0)
|
||||||
(x: 0.0)'''
|
(x: 0.0)'''
|
||||||
cmd: "nim cpp $file"
|
|
||||||
disabled: "true"
|
disabled: "true"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
23
tests/errmsgs/tmake_tuple_visible.nim
Normal file
23
tests/errmsgs/tmake_tuple_visible.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
discard """
|
||||||
|
errormsg: '''got (tuple of (type NimEdAppWindow, int))'''
|
||||||
|
line: 22
|
||||||
|
nimout: '''got (tuple of (type NimEdAppWindow, int))
|
||||||
|
but expected one of:
|
||||||
|
template xxx(tn: typedesc; i: int)'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
NimEdAppWindow = ptr NimEdAppWindowObj
|
||||||
|
NimEdAppWindowObj = object
|
||||||
|
i: int
|
||||||
|
|
||||||
|
template gDefineTypeExtended*(tn: typeDesc) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
gDefineTypeExtended (NimEdAppWindow)
|
||||||
|
|
||||||
|
template xxx*(tn: typeDesc, i: int) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
xxx (NimEdAppWindow, 0)
|
||||||
|
# bug #6776
|
||||||
|
|
@ -5,7 +5,8 @@ discard """
|
||||||
6
|
6
|
||||||
abcdefghijklmnopqrstuvwxyz
|
abcdefghijklmnopqrstuvwxyz
|
||||||
145 23
|
145 23
|
||||||
3'''
|
3
|
||||||
|
2'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
|
|
@ -122,3 +123,21 @@ var testTry =
|
||||||
PFooBase(field: 5)
|
PFooBase(field: 5)
|
||||||
|
|
||||||
echo(testTry.field)
|
echo(testTry.field)
|
||||||
|
|
||||||
|
# bug #6166
|
||||||
|
|
||||||
|
proc quo(op: proc (x: int): bool): int =
|
||||||
|
result =
|
||||||
|
if op(3):
|
||||||
|
2
|
||||||
|
else:
|
||||||
|
0
|
||||||
|
|
||||||
|
echo(
|
||||||
|
if true:
|
||||||
|
quo do (a: int) -> bool:
|
||||||
|
a mod 2 != 0
|
||||||
|
else:
|
||||||
|
quo do (a: int) -> bool:
|
||||||
|
a mod 3 != 0
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
exitcode: 0
|
exitcode: 0
|
||||||
|
disabled: '''true'''
|
||||||
"""
|
"""
|
||||||
import moverloading_typedesc
|
import moverloading_typedesc
|
||||||
import tables
|
import tables
|
||||||
|
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
discard """
|
|
||||||
file: "tromans.nim"
|
|
||||||
output: "success"
|
|
||||||
"""
|
|
||||||
import
|
|
||||||
strutils
|
|
||||||
|
|
||||||
## Convert an integer to a Roman numeral
|
|
||||||
# See http://en.wikipedia.org/wiki/Roman_numerals for reference
|
|
||||||
|
|
||||||
proc raiseInvalidValue(msg: string) {.noreturn.} =
|
|
||||||
# Yes, we really need a shorthand for this code...
|
|
||||||
var e: ref EInvalidValue
|
|
||||||
new(e)
|
|
||||||
e.msg = msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
# I should use a class, perhaps.
|
|
||||||
# --> No. Why introduce additional state into such a simple and nice
|
|
||||||
# interface? State is evil. :D
|
|
||||||
|
|
||||||
proc RomanToDecimal(romanVal: string): int =
|
|
||||||
result = 0
|
|
||||||
var prevVal = 0
|
|
||||||
for i in countdown(romanVal.len - 1, 0):
|
|
||||||
var val = 0
|
|
||||||
case romanVal[i]
|
|
||||||
of 'I', 'i': val = 1
|
|
||||||
of 'V', 'v': val = 5
|
|
||||||
of 'X', 'x': val = 10
|
|
||||||
of 'L', 'l': val = 50
|
|
||||||
of 'C', 'c': val = 100
|
|
||||||
of 'D', 'd': val = 500
|
|
||||||
of 'M', 'm': val = 1000
|
|
||||||
else: raiseInvalidValue("Incorrect character in roman numeral! (" &
|
|
||||||
$romanVal[i] & ")")
|
|
||||||
if val >= prevVal:
|
|
||||||
inc(result, val)
|
|
||||||
else:
|
|
||||||
dec(result, val)
|
|
||||||
prevVal = val
|
|
||||||
|
|
||||||
proc DecimalToRoman(decValParam: int): string =
|
|
||||||
# Apparently numbers cannot be above 4000
|
|
||||||
# Well, they can be (using overbar or parenthesis notation)
|
|
||||||
# but I see little interest (beside coding challenge) in coding them as
|
|
||||||
# we rarely use huge Roman numeral.
|
|
||||||
const romanComposites = [
|
|
||||||
("M", 1000), ("CM", 900),
|
|
||||||
("D", 500), ("CD", 400), ("C", 100),
|
|
||||||
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
|
|
||||||
("V", 5), ("IV", 4), ("I", 1)]
|
|
||||||
if decValParam < 1 or decValParam > 3999:
|
|
||||||
raiseInvalidValue("number not representable")
|
|
||||||
result = ""
|
|
||||||
var decVal = decValParam
|
|
||||||
for key, val in items(romanComposites):
|
|
||||||
while decVal >= val:
|
|
||||||
dec(decVal, val)
|
|
||||||
result.add(key)
|
|
||||||
|
|
||||||
for i in 1..100:
|
|
||||||
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
|
|
||||||
|
|
||||||
for i in items([1238, 1777, 3830, 2401, 379, 33, 940, 3973]):
|
|
||||||
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
|
|
||||||
|
|
||||||
echo "success" #OUT success
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
47
tests/overload/tstatic_with_converter.nim
Normal file
47
tests/overload/tstatic_with_converter.nim
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
9.0'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
### bug #6773
|
||||||
|
|
||||||
|
{.emit: """ /*INCLUDESECTION*/
|
||||||
|
typedef double cimported;
|
||||||
|
|
||||||
|
cimported set1_imported(double x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""}
|
||||||
|
|
||||||
|
type vfloat{.importc: "cimported".} = object
|
||||||
|
|
||||||
|
proc set1(a: float): vfloat {.importc: "set1_imported".}
|
||||||
|
|
||||||
|
converter scalar_to_vector(x: float): vfloat =
|
||||||
|
set1(x)
|
||||||
|
|
||||||
|
proc sqrt(x: vfloat): vfloat =
|
||||||
|
x
|
||||||
|
|
||||||
|
proc pow(x, y: vfloat): vfloat =
|
||||||
|
y
|
||||||
|
|
||||||
|
proc `^`(x: vfloat, exp: static[int]): vfloat =
|
||||||
|
when exp == 0:
|
||||||
|
1.0
|
||||||
|
else:
|
||||||
|
x
|
||||||
|
|
||||||
|
proc `^`(x: vfloat, exp: static[float]): vfloat =
|
||||||
|
when exp == 0.5:
|
||||||
|
sqrt(x)
|
||||||
|
else:
|
||||||
|
pow(x, exp)
|
||||||
|
|
||||||
|
proc `$`(x: vfloat): string =
|
||||||
|
let y = cast[ptr float](unsafeAddr x)
|
||||||
|
echo y[]
|
||||||
|
|
||||||
|
let x = set1(9.0)
|
||||||
|
echo x^0.5
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue