Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Araq
2017-11-21 01:42:58 +01:00
115 changed files with 1279 additions and 1542 deletions

4
.gitignore vendored
View File

@@ -57,3 +57,7 @@ dist/
# Private directories and files (IDEs)
.*/
~*
# testament cruft
testresults/
test.txt

View File

@@ -2,9 +2,7 @@
### 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 can! This means ``$`` for arrays can finally exist
in ``system.nim`` and do the right thing.
@@ -16,7 +14,6 @@
module.
- The overloading rules changed slightly so that constrained generics are
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
recursive types can be created across module boundaries. See
[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``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
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:
.. code-block:: nim
@@ -74,3 +69,26 @@ This now needs to be written as:
var a = -5
for i in a..b:
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)

View File

@@ -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 genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
origTy: PType) =
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
var test, u, v: TLoc
for i in countup(1, sonsLen(e) - 1):
var it = e.sons[i]
@@ -811,12 +810,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
assert(disc.kind == nkSym)
initLoc(test, locNone, it, OnStack)
initLocExpr(p, it.sons[1], u)
var o = obj
let d = lookupFieldAgain(p, origTy, disc.sym, o)
initLoc(v, locExpr, disc, OnUnknown)
v.r = o
v.r = obj
v.r.add(".")
v.r.add(d.loc.r)
v.r.add(disc.sym.loc.r)
genInExprAux(p, it, u, v, test)
let id = nodeTableTestOrSet(p.module.dataCache,
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:
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))
putIntoDest(p, d, e.sons[0], r, a.storage)
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: internalError(e.info, "genObjConstr")
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, field.loc.r)
tmp2.k = locTemp
@@ -1645,8 +1642,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, d, e, "(($1) ($2))" %
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
else:
putIntoDest(p, d, e, "(($1) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
# 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) =
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)]
else:
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:
if mapType(t) == ctArray: result = rope"{}"
else: result = rope"0"

View File

@@ -896,14 +896,15 @@ proc addIntTypes(result: var Rope) {.inline.} =
platform.CPU[targetCPU].intSize.rope])
proc getCopyright(cfile: Cfile): Rope =
const copyrightYear = "2017"
if optCompileOnly in gGlobalOptions:
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") %
[rope(VersionAsString)]
else:
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" &
"/* Compiled for: $2, $3, $4 */$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) =
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") %
[rope(VersionAsString)]

View File

@@ -53,8 +53,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# implementation
const
HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
"Copyright (c) 2006-" & CompileDate.substr(0, 3) & " by Andreas Rumpf\n"
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
"Copyright (c) 2006-2017 by Andreas Rumpf\n"
const
Usage = slurp"../doc/basicopt.txt".replace("//", "")

View File

@@ -608,6 +608,52 @@ proc generateJson*(d: PDoc, n: PNode) =
generateJson(d, lastSon(n.sons[0]))
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) =
const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
@@ -745,6 +791,21 @@ proc commandJson*() =
#echo getOutFile(gProjectFull, JsonExt)
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*() =
var content = mergeIndexes(gProjectFull).rope

View File

@@ -52,7 +52,7 @@ proc isLet(n: PNode): bool =
proc isVar(n: PNode): bool =
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 =
# 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)
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 =
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1

View File

@@ -376,8 +376,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
["", "", "($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
["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],

View File

@@ -186,12 +186,12 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "php":
gCmd = cmdCompileToPHP
commandCompileToJS(graph, cache)
of "doc":
of "doc0":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
commandDoc()
of "doc2":
of "doc2", "doc":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
defineSymbol("nimdoc")
@@ -217,6 +217,13 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
wantMainModule()
defineSymbol("nimdoc")
commandDoc2(graph, cache, true)
of "ctags":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()
defineSymbol("nimdoc")
commandTags()
of "buildindex":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)

View File

@@ -162,6 +162,7 @@ const
RodExt* = "rod"
HtmlExt* = "html"
JsonExt* = "json"
TagsExt* = "tags"
TexExt* = "tex"
IniExt* = "ini"
DefaultConfig* = "nim.cfg"

View File

@@ -7,8 +7,9 @@
# distribution, for details about the copyright.
#
## Plugin support for the Nim compiler. Right now they
## need to be build with the compiler, no DLL support.
## Plugin support for the Nim compiler. Right now plugins
## need to be built with the compiler only: plugins using
## DLLs or the FFI will not work.
import ast, semdata, idents

View File

@@ -606,13 +606,13 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkObjConstr:
result = copyTree(n)
for i in countup(1, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i].sons[1])
if a == nil: return nil
result.sons[i].sons[1] = a
incl(result.flags, nfAllConst)
#of nkObjConstr:
# result = copyTree(n)
# for i in countup(1, sonsLen(n) - 1):
# var a = getConstExpr(m, n.sons[i].sons[1])
# if a == nil: return nil
# result.sons[i].sons[1] = a
# incl(result.flags, nfAllConst)
of nkPar:
# tuple constructor
result = copyTree(n)

View File

@@ -127,7 +127,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
of "not":
return typeWithSonsResult(tyNot, @[operand])
of "name":
result = newStrNode(nkStrLit, operand.typeToString(preferName))
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
result.typ = newType(tyString, context)
result.info = traitCall.info
of "arity":

View File

@@ -1420,9 +1420,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: result = semGeneric(c, n, s, prev)
of nkDotExpr:
let typeExpr = semExpr(c, n)
if typeExpr.typ.kind == tyFromExpr:
return typeExpr.typ
if typeExpr.typ.kind != tyTypeDesc:
if typeExpr.typ.isNil:
localError(n.info, "object constructor needs an object type;" &
" 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)
result = errorType(c)
else:

View File

@@ -1385,7 +1385,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
# XXX: This is very hacky. It should be moved back into liftTypeParam
if x.kind in {tyGenericInst, tyArray} 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)
return typeRel(c, inst, a)
@@ -1613,7 +1613,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone
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)
if result != isNone:
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])

View File

@@ -365,7 +365,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[0] = m.sons[0]
result = PTransNode(n.sons[0])
if n.typ.kind != tyOpenArray:
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
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)
n.sons[0].sons[1] = m.sons[0]
result = PTransNode(n.sons[0])
if n.typ.kind != tyOpenArray:
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ
else:
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0])
if n.typ.kind != tyOpenArray:
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ
proc generateThunk(prc: PNode, dest: PType): PNode =

View File

@@ -14,7 +14,8 @@ import
type
TPreferedDesc* = enum
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg,
preferTypeName
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
template `$`*(typ: PType): string = typeToString(typ)
@@ -394,7 +395,7 @@ const
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
"void"]
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg}
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
tc.sons.safeAdd concrete
@@ -420,7 +421,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
sfAnon notin t.sym.flags:
if t.kind == tyInt and isIntLit(t):
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
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
result.add ": "
@@ -518,7 +519,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = "openarray[" & typeToString(t.sons[0]) & ']'
of tyDistinct:
result = "distinct " & typeToString(t.sons[0],
if prefer == preferModuleInfo: preferModuleInfo else: preferName)
if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
if t.n != nil:
@@ -532,7 +533,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
elif sonsLen(t) == 0:
result = "tuple[]"
else:
result = "("
if prefer == preferTypeName: result = "("
else: result = "tuple of ("
for i in countup(0, sonsLen(t) - 1):
add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ")

View File

@@ -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 nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch.
gen(c, n.sons[0].sons[1], dest)
# This is "when nimvm" node. Chose the first branch.
gen(c, n.sons[0].sons[1], dest)
of nkCaseStmt: genCase(c, n, dest)
of nkWhileStmt:
unused(n, dest)
@@ -1810,7 +1810,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkVarSection, nkLetSection:
unused(n, dest)
genVarSection(c, n)
of declarativeDefs:
of declarativeDefs, nkMacroDef:
unused(n, dest)
of nkLambdaKinds:
#let s = n.sons[namePos].sym

View File

@@ -7,7 +7,8 @@ Advanced commands:
//rst2html convert a reStructuredText file to HTML
//rst2tex convert a reStructuredText file to TeX
//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
//run run the project (with Tiny C backend; buggy!)
//genDepend generate a DOT file containing the

View File

@@ -5,7 +5,6 @@
Command:
//compile, c compile project with default code generator (C)
//doc generate the documentation for inputfile
//doc2 generate the documentation for inputfile
Arguments:
arguments are passed to the program being run (if --run option is selected)

View File

@@ -64,7 +64,6 @@ Core
* `cpuinfo <cpuinfo.html>`_
This module implements procs to determine the number of CPUs / cores.
Collections and algorithms
--------------------------
@@ -182,6 +181,12 @@ Generic Operating System Services
This module implements asynchronous file reading and writing using
``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
--------------

View File

@@ -9,26 +9,26 @@ The following example shows a generic binary tree can be modelled:
.. code-block:: nim
type
BinaryTreeObj[T] = object # BinaryTreeObj is a generic type with
# with generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
BinaryTree[T] = ref BinaryTreeObj[T] # a shorthand for notational convenience
BinaryTree*[T] = ref object # BinaryTree is a generic type with
# generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
proc newNode[T](data: T): BinaryTree[T] = # constructor for a node
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
new(result)
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:
root = n
else:
var it = root
while it != nil:
var c = cmp(it.data, n.data) # compare the data items; uses
# the generic ``cmp`` proc that works for
# any type that has a ``==`` and ``<``
# operator
# compare the data items; uses the generic ``cmp`` proc
# that works for any type that has a ``==`` and ``<`` operator
var c = cmp(it.data, n.data)
if c < 0:
if it.le == nil:
it.le = n
@@ -40,20 +40,28 @@ The following example shows a generic binary tree can be modelled:
return
it = it.ri
iterator inorder[T](root: BinaryTree[T]): T =
# inorder traversal of a binary tree
# recursive iterators are not yet implemented, so this does not work in
# the current compiler!
if root.le != nil: yield inorder(root.le)
yield root.data
if root.ri != nil: yield inorder(root.ri)
proc add*[T](root: var BinaryTree[T], data: T) =
# convenience proc:
add(root, newNode(data))
iterator preorder*[T](root: BinaryTree[T]): T =
# Preorder traversal of a binary tree.
# 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
root: BinaryTree[string] # instantiate a BinaryTree with the type string
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and
add(root, newNode("world")) # ``add``
for str in inorder(root):
writeLine(stdout, str)
root: BinaryTree[string] # instantiate a BinaryTree with ``string``
add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
add(root, "world") # instantiates the second ``add`` proc
for str in preorder(root):
stdout.writeLine(str)
Is operator

View File

@@ -102,6 +102,14 @@ collector to not consider objects of this type as part of a cycle:
left, right: Node
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
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

View File

@@ -5,8 +5,7 @@ Example:
.. code-block:: nim
type # example demonstrating mutually recursive types
Node = ref NodeObj # a traced pointer to a NodeObj
NodeObj = object
Node = ref object # an object managed by the garbage collector (ref)
le, ri: Node # left and right subtrees
sym: ref Sym # leaves contain a reference to a Sym

View File

@@ -1511,8 +1511,7 @@ operators perform implicit dereferencing operations for reference types:
.. code-block:: nim
type
Node = ref NodeObj
NodeObj = object
Node = ref object
le, ri: Node
data: int
var

View File

@@ -104,15 +104,14 @@ Example:
.. code-block:: nim
type
Node = ref NodeObj # a traced reference to a NodeObj
NodeObj = object
Node = ref object # a reference to an object with the following field:
le, ri: Node # left and right subtrees
sym: ref Sym # leaves contain a reference to a Sym
Sym = object # a symbol
name: string # the symbol's name
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
@@ -155,8 +154,7 @@ An example:
nkAdd, # an addition
nkSub, # a subtraction
nkIf # an if statement
Node = ref NodeObj
NodeObj = object
Node = ref object
case kind: NodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
@@ -482,11 +480,10 @@ containers:
.. code-block:: nim
type
BinaryTreeObj[T] = object # BinaryTree is a generic type with
# with generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
BinaryTree*[T] = ref BinaryTreeObj[T] # type that is exported
BinaryTree*[T] = ref object # BinaryTree is a generic type with
# generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node

View File

@@ -85,7 +85,7 @@
<int key="IBUITag">2</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<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">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>

View File

@@ -42,7 +42,7 @@ function myAdd(x, y: longint): longint; cdecl; external;
procedure TForm1.FormCreate(Sender: TObject);
begin
// we initialize the Nimrod data structures here:
// we initialize the Nim data structures here:
NimMain();
end;

View File

@@ -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.
path="../nim_backend"

View File

@@ -2,7 +2,7 @@ The cross platform calculator illustrates how to use Nim to create a backend
called by different native user interfaces.
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
platforms.

View File

@@ -4,9 +4,8 @@
import db_sqlite, parseutils, strutils, times
type
TTodo* = object
Todo* = object
## A todo object holding the information serialized to the database.
id: int64 ## Unique identifier of the object in the
## database, use the getId() accessor to read it.
@@ -17,7 +16,7 @@ type
## outside of this module, use the
## getModificationDate accessor.
TPagedParams* = object
PagedParams* = object
## Contains parameters for a query, initialize default values with
## initDefaults().
pageSize*: int64 ## Lines per returned query page, -1 for
@@ -27,11 +26,10 @@ type
showUnchecked*: bool ## Get unchecked objects.
showChecked*: bool ## Get checked objects.
# - General procs
#
proc initDefaults*(params: var TPagedParams) =
## Sets sane defaults for a TPagedParams object.
proc initDefaults*(params: var PagedParams) =
## Sets sane defaults for a PagedParams object.
##
## Note that you should always provide a non zero pageSize, either a specific
## positive value or negative for unbounded query results.
@@ -41,7 +39,6 @@ proc initDefaults*(params: var TPagedParams) =
params.showUnchecked = true
params.showChecked = false
proc openDatabase*(path: string): DbConn =
## Creates or opens the sqlite3 database.
##
@@ -56,16 +53,14 @@ proc openDatabase*(path: string): DbConn =
desc TEXT NOT NULL,
modification_date INTEGER NOT NULL,
CONSTRAINT Todos UNIQUE (id))"""
db_sqlite.exec(conn, query)
result = conn
# - Procs related to Todo objects
# - Procs related to TTodo objects
#
proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
modificationDate: Time): TTodo =
## Returns an initialized TTodo object created from database parameters.
modificationDate: Time): Todo =
## Returns an initialized Todo object created from database parameters.
##
## The proc assumes all values are right. Note this proc is NOT exported.
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.modificationDate = modificationDate
proc getId*(todo: TTodo): int64 =
proc getId*(todo: Todo): int64 =
## Accessor returning the value of the private id property.
return todo.id
proc getModificationDate*(todo: TTodo): Time =
## Returns the last modification date of a TTodo entry.
proc getModificationDate*(todo: Todo): Time =
## Returns the last modification date of a Todo entry.
return todo.modificationDate
proc update*(todo: var TTodo; conn: DbConn): bool =
proc update*(todo: var Todo; conn: DbConn): bool =
## Checks the database for the object and refreshes its variables.
##
## 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.
## 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
## TTodo object.
## Todo object.
assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
let query = sql"""SELECT desc, priority, is_done, modification_date
FROM Todos WHERE id = ?"""
try:
let rows = conn.getAllRows(query, $todo.id)
if len(rows) < 1:
@@ -111,8 +102,7 @@ proc update*(todo: var TTodo; conn: DbConn): bool =
except:
echo("Something went wrong selecting for id " & $todo.id)
proc save*(todo: var TTodo; conn: DbConn): bool =
proc save*(todo: var Todo; conn: DbConn): bool =
## Saves the current state of text, priority and isDone to the database.
##
## 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 = ?"""
rowsUpdated = conn.execAffectedRows(query, $todo.text,
$todo.priority, $todo.isDone, $int(currentDate), $todo.id)
if 1 == rowsUpdated:
todo.modificationDate = currentDate
result = true
# - 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.
##
## 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)
VALUES (?, 'false', ?, ?)"""
todoId = conn.insertId(query, priority, text, $int(currentDate))
result = initFromDB(todoId, text, priority, false, currentDate)
proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
## 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 = ?"""
result = conn.execAffectedRows(query, $todoId)
proc getNumEntries*(conn: DbConn): int =
## 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")
result = -1
proc getPagedTodos*(conn: DbConn; params: TPagedParams;
page = 0'i64): seq[TTodo] =
proc getPagedTodos*(conn: DbConn; params: PagedParams; page = 0'i64): seq[Todo] =
## Returns the todo entries for a specific page.
##
## 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
## affected by the TPagedParams, which should have sane values (call
## affected by the PagedParams, which should have sane values (call
## initDefaults).
assert(page >= 0, "You should request a page zero or bigger than zero")
result = @[]
# 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
let
order_by = [
if params.priorityAscending: "ASC" else: "DESC",
if params.dateAscending: "ASC" else: "DESC"]
query = sql("""SELECT id, desc, priority, is_done, modification_date
FROM Todos
WHERE is_done = ? OR is_done = ?
ORDER BY priority $1, modification_date $2, id DESC
LIMIT ? * ?,?""" % order_by)
args = @[$params.showChecked, $(not params.showUnchecked),
$params.pageSize, $page, $params.pageSize]
#echo("Query " & string(query))
#echo("args: " & args.join(", "))
var newId: BiggestInt
for row in conn.fastRows(query, args):
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,
row[3].parseBool, Time(row[4].parseInt)))
proc getTodo*(conn: DbConn; todoId: int64): ref TTodo =
## Returns a reference to a TTodo or nil if the todo could not be found.
var tempTodo: TTodo
proc getTodo*(conn: DbConn; todoId: int64): ref Todo =
## Returns a reference to a Todo or nil if the todo could not be found.
var tempTodo: Todo
tempTodo.id = todoId
if tempTodo.update(conn):
new(result)

View File

@@ -2,8 +2,7 @@
import backend, db_sqlite, strutils, times
proc showPagedResults(conn: DbConn; params: TPagedParams) =
proc showPagedResults(conn: DbConn; params: PagedParams) =
## 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,
@@ -11,7 +10,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) =
var
page = 0'i64
rows = conn.getPagedTodos(params)
while rows.len > 0:
echo("page " & $page)
for row in rows:
@@ -25,7 +23,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) =
else:
break
proc dumTest() =
let conn = openDatabase("todo.sqlite3")
try:
@@ -35,10 +32,8 @@ proc dumTest() =
# Fill some dummy rows if there are not many entries yet.
discard conn.addTodo(3, "Filler1")
discard conn.addTodo(4, "Filler2")
var todo = conn.addTodo(2, "Testing")
echo("New todo added with id " & $todo.getId)
# Try changing it and updating the database.
var clonedTodo = conn.getTodo(todo.getId)[]
assert(clonedTodo.text == todo.text, "Should be equal")
@@ -49,13 +44,11 @@ proc dumTest() =
echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone])
else:
assert(false, "Uh oh, I wasn't expecting that!")
# Verify our cloned copy is different but can be updated.
assert(clonedTodo.text != todo.text, "Should be different")
discard clonedTodo.update(conn)
assert(clonedTodo.text == todo.text, "Should be equal")
var params: TPagedParams
var params: PagedParams
params.initDefaults
conn.showPagedResults(params)
conn.deleteTodo(todo.getId)
@@ -66,7 +59,6 @@ proc dumTest() =
echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone])
else:
echo("Can't update object $1 from db!" % $todo.getId)
# Try to list content in a different way.
params.pageSize = 5
params.priorityAscending = true
@@ -77,7 +69,6 @@ proc dumTest() =
conn.close
echo("Database closed")
# Code that will be run only on the commandline.
when isMainModule:
dumTest()

View File

@@ -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.
path="../nim_backend"

View File

@@ -18,8 +18,8 @@ Commands:
-h, --help shows this help
List options (optional):
-p=+|- Sorts list by ascending|desdencing priority. Default:desdencing.
-m=+|- Sorts list by ascending|desdencing date. Default:desdencing.
-p=+|- Sorts list by ascending|descending priority. Default:descending.
-m=+|- Sorts list by ascending|descending date. Default:descending.
-t Show checked entries. By default they are not shown.
-z Hide unchecked entries. By default they are shown.
@@ -33,7 +33,7 @@ Examples:
"""
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.
cmdCheck # User wants to check 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.
cmdList # User wants to list contents.
TParamConfig = object
ParamConfig = object
# 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.
addText: seq[string] # Only valid with cmdAdd, stores todo text.
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 TParamConfig) =
proc initDefaults(params: var ParamConfig) =
## Initialises defaults value in the structure.
##
## Most importantly we want to have an empty list for addText.
params.listParams.initDefaults
params.addText = @[]
proc abort(message: string, value: int) =
# Simple wrapper to abort also displaying the help to the user.
stdout.write(USAGE)
quit(message, value)
template parseTodoIdAndSetCommand(newCommand: TCommand): stmt =
template parseTodoIdAndSetCommand(newCommand: Command): untyped =
## Helper to parse a big todo identifier into todoId and set command.
try:
let numChars = val.parseBiggestInt(newId)
@@ -75,8 +72,7 @@ template parseTodoIdAndSetCommand(newCommand: TCommand): stmt =
except OverflowError:
raise newException(ValueError, "Value $1 too big" % val)
template verifySingleCommand(actions: stmt): stmt =
template verifySingleCommand(actions: typed): typed =
## Helper to make sure only one command has been specified so far.
if specifiedCommand:
abort("Only one command can be specified at a time! (extra:$1)" % [key], 2)
@@ -84,7 +80,6 @@ template verifySingleCommand(actions: stmt): stmt =
actions
specifiedCommand = true
proc parsePlusMinus(val: string, debugText: string): bool =
## Helper to process a plus or minus character from the commandline.
##
@@ -100,11 +95,10 @@ proc parsePlusMinus(val: string, debugText: string): bool =
else:
abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4)
proc parseCmdLine(): TParamConfig =
proc parseCmdLine(): ParamConfig =
## 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.
var
specifiedCommand = false
@@ -112,15 +106,12 @@ proc parseCmdLine(): TParamConfig =
p = initOptParser()
key, val: TaintedString
newId: BiggestInt
result.initDefaults
try:
while true:
next(p)
key = p.key
val = p.val
case p.kind
of cmdArgument:
if specifiedCommand and cmdAdd == result.command:
@@ -180,17 +171,13 @@ proc parseCmdLine(): TParamConfig =
break
except ValueError:
abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7)
if not specifiedCommand:
abort("Didn't specify any command.", 8)
if cmdAdd == result.command and result.addText.len < 1:
abort("Used the add command, but provided no text/description.", 9)
if usesListParams and cmdList != result.command:
abort("Used list options, but didn't specify the list command.", 10)
proc generateDatabaseRows(conn: DbConn) =
## Adds some rows to the database ignoring errors.
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")
echo("Generated some entries, they were added to your database.")
proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) =
proc listDatabaseContents(conn: DbConn; listParams: PagedParams) =
## Dumps the database contents formatted to the standard output.
##
## Pass the list/filter parameters parsed from the commandline.
var params = listParams
params.pageSize = -1
let todos = conn.getPagedTodos(params)
if todos.len < 1:
echo("Database empty")
return
echo("Todo id, is done, priority, last modification date, text:")
# First detect how long should be our columns for formatting.
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[1] = max(cols[1], ($todo.priority).len)
cols[2] = max(cols[2], ($todo.getModificationDate).len)
# Now dump all the rows using the calculated alignment sizes.
for todo in todos:
echo("$1 $2 $3, $4, $5" % [
@@ -238,7 +221,6 @@ proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) =
($todo.getModificationDate).align(cols[2]),
todo.text])
proc deleteOneTodo(conn: DbConn; todoId: int64) =
## Deletes a single todo entry from the database.
let numDeleted = conn.deleteTodo(todoId)
@@ -247,7 +229,6 @@ proc deleteOneTodo(conn: DbConn; todoId: int64) =
else:
quit("Couldn't delete todo id " & $todoId, 11)
proc deleteAllTodos(conn: DbConn) =
## Deletes all the contents from the database.
##
@@ -256,43 +237,35 @@ proc deleteAllTodos(conn: DbConn) =
## ourselfves to the API exported by backend.
var
counter: int64
params: TPagedParams
params: PagedParams
params.initDefaults
params.pageSize = -1
params.showUnchecked = true
params.showChecked = true
let todos = conn.getPagedTodos(params)
for todo in todos:
if conn.deleteTodo(todo.getId) > 0:
counter += 1
else:
quit("Couldn't delete todo id " & $todo.getId, 12)
echo("Deleted $1 todo entries from database." % $counter)
proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) =
## Changes the check state of a todo entry to the specified value.
let
newState = if value: "checked" else: "unchecked"
todo = conn.getTodo(todoId)
if todo == nil:
quit("Can't modify todo id $1, its not in the database." % $todoId, 13)
if todo[].isDone == value:
echo("Todo id $1 was already set to $2." % [$todoId, newState])
return
todo[].isDone = value
if todo[].save(conn):
echo("Todo id $1 set to $2." % [$todoId, newState])
else:
quit("Error updating todo id $1 to $2." % [$todoId, newState])
proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) =
## 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'." % [
$todo.getId, $todo.priority, todo.text])
when isMainModule:
## Main entry point.
let
opt = parseCmdLine()
dbPath = getConfigDir() / "nimtodo.sqlite3"
if not dbPath.existsFile:
createDir(getConfigDir())
echo("No database found at $1, it will be created for you." % dbPath)
let conn = openDatabase(dbPath)
try:
case opt.command

View File

@@ -14,6 +14,6 @@ generation switch can be used to fill the database with some basic todo entries
you can play with.
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
the command line with 'nim c -r nimtodo.nim'.

View File

@@ -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.
This example builds on the knowledge learned from the cross_calculator example.

View File

@@ -3,7 +3,6 @@ import re
for x in lines("myfile.txt"):
if x =~ re"(\w+)=(.*)":
echo "Key: ", matches[0],
" Value: ", matches[1]
echo "Key: ", matches[0], " Value: ", matches[1]

View File

@@ -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.
Copyright (c) 2004-2012 Andreas Rumpf.

View File

@@ -1,20 +1,20 @@
import strutils
template html(name: expr, matter: stmt) {.immediate.} =
template html(name, matter: untyped) =
proc name(): string =
result = "<html>"
matter
result.add("</html>")
template nestedTag(tag: expr) {.immediate.} =
template tag(matter: stmt) {.immediate.} =
template nestedTag(tag: untyped) =
template tag(matter: typed) =
result.add("<" & astToStr(tag) & ">")
matter
result.add("</" & astToStr(tag) & ">")
template simpleTag(tag: expr) {.immediate.} =
template tag(matter: expr) {.immediate.} =
template simpleTag(tag: untyped) =
template tag(matter: untyped) =
result.add("<$1>$2</$1>" % [astToStr(tag), matter])
nestedTag body

View File

@@ -5,7 +5,7 @@ import macros
proc invalidFormatString() =
echo "invalidFormatString"
template formatImpl(handleChar: expr) =
template formatImpl(handleChar: untyped) =
var i = 0
while i < f.len:
if f[i] == '$':
@@ -24,11 +24,11 @@ template formatImpl(handleChar: expr) =
i += 1
proc `%`*(f: string, a: openArray[string]): string =
template identity(x: expr): expr = x
template identity(x: untyped): untyped = x
result = ""
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)
#newCall("&")
let f = f.strVal

View File

@@ -1,7 +1,7 @@
import macros
macro check(ex: expr): stmt =
macro check(ex: untyped): typed =
var info = ex.lineinfo
var expString = ex.toStrLit
result = quote do:

View File

@@ -1,8 +1,9 @@
template htmlTag(tag: expr) {.immediate.} =
template htmlTag(tag: untyped) =
proc tag(): string = "<" & astToStr(tag) & ">"
htmlTag(br)
htmlTag(html)
echo br()
echo html()

View File

@@ -473,7 +473,7 @@ proc temp(args: string) =
# commit.
let (bootArgs, programArgs) = splitArgs(args)
let nimexec = findNim()
exec(nimexec & " c " & bootArgs & " compiler" / "nim", 125)
exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125)
copyExe(output, finalDest)
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)
try:
withDir(d):
temp"-d:debug"
temp""
copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
exec(cmd)
finally:

View File

@@ -113,7 +113,9 @@ type
type
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
NimSym* = ref NimSymObj
@@ -129,7 +131,11 @@ const
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
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`
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 newNimNode*(kind: NimNodeKind,
lineInfoFrom: NimNode=nil): NimNode
lineInfoFrom: NimNode = nil): NimNode
{.magic: "NNewNimNode", noSideEffect.}
## 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.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = !i
result.ident = toNimIdent i
type
@@ -400,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
##
## .. code-block:: nim
##
## macro check(ex: expr): stmt =
## macro check(ex: untyped): typed =
## # this is a simplified version of the check macro from the
## # unittest module.
##
@@ -477,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
result = newNimNode(nnkCharLit)
result.intVal = ord(c)
proc newLit*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkIntLit)
@@ -581,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
proc newLit*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree
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.} =
## 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 nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
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 nnkNone: assert false
else:
@@ -640,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
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 nnkNone: assert false
else:
@@ -664,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## .. code-block:: nim
## nnkStmtList.newTree(
## nnkCommand.newTree(
## newIdentNode(!"echo"),
## newIdentNode("echo"),
## newLit("Hello world")
## )
## )
@@ -718,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
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 nnkNone: assert false
else:
@@ -898,6 +903,48 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
for i in branches:
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.}=
## Copy all children from `src` to `dest`
@@ -1015,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
##
## .. code-block:: nim
## var res = findChild(n, it.kind == nnkPostfix and
## it.basename.ident == !"foo")
## it.basename.ident == toNimIdent"foo")
block:
var res: NimNode
for it in n.children:
@@ -1049,7 +1096,7 @@ proc basename*(a: NimNode): NimNode =
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
case a.kind
of nnkIdent: macros.`ident=`(a, !val)
of nnkIdent: macros.`ident=`(a, toNimIdent val)
of nnkPostfix, nnkPrefix: a[1] = ident(val)
else:
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.
case node.kind
of nnkIdent:
result = node.ident == !s
result = node.ident == toNimIdent s
of nnkSym:
result = eqIdent($node.symbol, s)
of nnkOpenSymChoice, nnkClosedSymChoice:

View File

@@ -3,12 +3,12 @@ export nativesockets
{.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".}
newNativeSocket(domain, sockType, protocol)
template newRawSocket*(domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): expr =
protocol: Protocol = IPPROTO_TCP): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol)

View File

@@ -58,15 +58,18 @@ type
socket: AsyncSocket
reuseAddr: bool
reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
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.
new result
result.reuseAddr = reuseAddr
result.reusePort = reusePort
result.maxBody = maxBody
proc addHeaders(msg: var string, headers: HttpHeaders) =
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: " &
protocol)
result.orig = protocol
i.inc protocol.parseInt(result.major, i)
i.inc protocol.parseSaturatedNatural(result.major, i)
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] =
client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string,
callback: proc (request: Request):
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request):
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:
# GET /path HTTP/1.1
# Header: val
# \n
request.headers.clear()
request.body = ""
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
req.mget()
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
for i in 0..1:
lineFut.mget().setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
# GET /path HTTP/1.1
# Header: val
# \n
request.headers.clear()
request.body = ""
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
if lineFut.mget == "":
client.close()
return
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
for i in 0..1:
lineFut.mget().setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
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"):
if lineFut.mget == "":
client.close()
return
# Persistent connections
if (request.protocol == HttpVer11 and
request.headers.getOrDefault("connection").normalize != "close") or
(request.protocol == HttpVer10 and
request.headers.getOrDefault("connection").normalize == "keep-alive"):
# 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()
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
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)
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,
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
address = "") {.async.} =
@@ -280,7 +299,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
# TODO: Causes compiler crash.
#var (address, client) = 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.repr)

View File

@@ -472,27 +472,13 @@ proc stripAwait(node: NimNode): NimNode =
proc splitParamType(paramType: NimNode, async: bool): NimNode =
result = paramType
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
let firstType = paramType[1]
let firstTypeName = $firstType.ident
let secondType = paramType[2]
let secondTypeName = $secondType.ident
let firstAsync = "async" in ($paramType[1].ident).normalize
let secondAsync = "async" in ($paramType[2].ident).normalize
# Make sure that at least one has the name `async`, otherwise we shouldn't
# touch it.
if not ("async" in firstTypeName.normalize or
"async" in secondTypeName.normalize):
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]
if firstAsync:
result = paramType[if async: 1 else: 2]
elif secondAsync:
result = paramType[if async: 2 else: 1]
proc stripReturnType(returnType: NimNode): NimNode =
# Strip out the 'Future' from 'Future[T]'.
@@ -535,4 +521,4 @@ macro multisync*(prc: untyped): untyped =
let (sync, asyncPrc) = splitProc(prc)
result = newStmtList()
result.add(asyncSingleProc(asyncPrc))
result.add(sync)
result.add(sync)

View File

@@ -21,24 +21,18 @@ proc openDefaultBrowser*(url: string) =
## opens `url` with the user's default browser. This does not block.
##
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
## command is used. Under Unix, it is checked if ``gnome-open`` exists and
## used if it does. Next attempt is ``kde-open``, then ``xdg-open``.
## Otherwise the environment variable ``BROWSER`` is used to determine the
## default browser to use.
## command is used. Under Unix, it is checked if ``xdg-open`` exists and
## used if it does. Otherwise the environment variable ``BROWSER`` is
## used to determine the default browser to use.
when defined(windows):
when useWinUnicode:
var o = newWideCString("open")
var u = newWideCString(url)
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
else:
discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
var o = newWideCString("open")
var u = newWideCString(url)
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
elif defined(macosx):
discard execShellCmd("open " & quoteShell(url))
else:
const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
var u = quoteShell(url)
for a in items(attempts):
if execShellCmd(a & u) == 0: return
if execShellCmd("xdg-open " & u) == 0: return
for b in getEnv("BROWSER").string.split(PathSep):
try:
# we use ``startProcess`` here because we don't want to block!

View File

@@ -20,6 +20,8 @@
include "system/inclrtl"
import macros
when not defined(nimhygiene):
{.pragma: dirty.}
@@ -704,6 +706,52 @@ template newSeqWith*(len: int, init: untyped): untyped =
result[i] = init
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:
import strutils
block: # concat test
@@ -992,5 +1040,11 @@ when isMainModule:
seq2D[0][1] = true
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):
echo "Finished doc tests"

View File

@@ -87,10 +87,10 @@ proc newSharedString*(s: string): SharedString =
result.len = len
when declared(atomicLoadN):
template load(x): expr = atomicLoadN(addr x, ATOMIC_SEQ_CST)
template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
else:
# XXX Fixme
template load(x): expr = x
template load(x): untyped = x
proc add*(s: var SharedString; t: cstring; len: Natural) =
if len == 0: return

View File

@@ -13,6 +13,15 @@ import strtabs, times
proc parseCookies*(s: string): StringTableRef =
## 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)
var i = 0
while true:

View File

@@ -197,4 +197,4 @@ macro dump*(x: typed): untyped =
let s = x.toStrLit
let r = quote do:
debugEcho `s`, " = ", `x`
return r
return r

View File

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

View File

@@ -43,6 +43,7 @@ const bsdPlatform = defined(macosx) or defined(freebsd) or
defined(dragonfly)
when defined(nimdoc):
type SocketHandle = int
type
Selector*[T] = ref object
## An object which holds descriptors to be checked for read/write status

View File

@@ -1849,8 +1849,8 @@ when isMainModule:
discard parseJson"""{ invalid"""
except:
discard
# memory diff should less than 2M
doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024)
# memory diff should less than 4M
doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
# test `$`

View File

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

View File

@@ -1195,14 +1195,15 @@ when defined(nimdoc):
## Returns the number of `command line arguments`:idx: given to the
## application.
##
## If your binary was called without parameters this will return zero. You
## can later query each individual paramater with `paramStr() <#paramStr>`_
## Unlike `argc`:idx: in C, if your binary was called without parameters this
## will return zero.
## You can query each individual paramater with `paramStr() <#paramStr>`_
## or retrieve all of them in one go with `commandLineParams()
## <#commandLineParams>`_.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared() <system.html#declared>`_.
## **Availability**: When generating a dynamic library (see --app:lib) on
## Posix this proc is not defined.
## Test for availability using `declared() <system.html#declared>`_.
## Example:
##
## .. code-block:: nim
@@ -1219,13 +1220,14 @@ when defined(nimdoc):
## `paramCount() <#paramCount>`_ with this proc you can call the
## 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
## this and call `getAppFilename() <#getAppFilename>`_ instead.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared() <system.html#declared>`_.
## **Availability**: When generating a dynamic library (see --app:lib) on
## Posix this proc is not defined.
## Test for availability using `declared() <system.html#declared>`_.
## Example:
##
## .. code-block:: nim

View File

@@ -841,7 +841,7 @@ elif not defined(useNimRtl):
var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
template chck(e: untyped) =
if e != 0'i32: raiseOSError(osLastError())
chck posix_spawn_file_actions_init(fops)

View File

@@ -252,6 +252,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
elif result != 0:
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
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
var
@@ -393,16 +418,43 @@ when isMainModule:
let input = "$test{} $this is ${an{ example}} "
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
assert toSeq(interpolatedFragments(input)) == expected
doAssert toSeq(interpolatedFragments(input)) == expected
var value = 0
discard parseHex("0x38", value)
assert value == 56
doAssert value == 56
discard parseHex("0x34", value)
assert value == 56 * 256 + 52
doAssert value == 56 * 256 + 52
value = -1
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.}

View File

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

View File

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

View File

@@ -1927,7 +1927,7 @@ type
{.deprecated: [TFloatFormat: FloatFormatMode].}
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16;
precision: range[-1..32] = 16;
decimalSep = '.'): string {.
noSideEffect, rtl, extern: "nsu$1".} =
## 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
## 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):
var res: cstring
case format
@@ -1961,7 +1961,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
buf {.noinit.}: array[0..2500, char]
L: cint
frmtstr[0] = '%'
if precision > 0:
if precision >= 0:
frmtstr[1] = '#'
frmtstr[2] = '.'
frmtstr[3] = '*'
@@ -1984,7 +1984,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
# but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalsep
else: result[i] = buf[i]
when defined(vcc):
when defined(windows):
# VS pre 2015 violates the C standard: "The exponent always contains at
# least two digits, and only as many more digits as necessary to
# represent the exponent." [C11 §7.21.6.1]
@@ -1995,7 +1995,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
result.setLen(result.len - 1)
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".} =
## 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
## 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:
##
## .. code-block:: nim
##
## let x = 123.456
## echo x.formatFloat() # 123.4560000000000
## echo x.formatFloat(ffDecimal, 4) # 123.4560
## echo x.formatFloat(ffScientific, 2) # 1.23e+02
## doAssert x.formatFloat() == "123.4560000000000"
## doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
##
result = formatBiggestFloat(f, format, precision, decimalSep)
@@ -2471,12 +2471,14 @@ when isMainModule:
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
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, ffScientific, 1, ',') in
["1,0e-11", "1,0e-011"]
# bug #6589
doAssert formatFloat(123.456, ffScientific, precision=0) in
["1.234560e+02", "1.234560e+002"]
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"

View File

@@ -21,7 +21,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
##
## proc `$`*(T: typedesc): string = name(T)
##
## template test(x): stmt =
## template test(x): typed =
## echo "type: ", type(x), ", value: ", x
##
## test 42

View File

@@ -308,11 +308,16 @@ proc `$`*(u: Uri): string =
result.add(":")
result.add(u.password)
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:
result.add(":")
result.add(u.port)
if u.path.len > 0:
if u.hostname.len > 0 and u.path[0] != '/':
result.add('/')
result.add(u.path)
if u.query.len > 0:
result.add("?")
@@ -483,6 +488,34 @@ when isMainModule:
let foo = parseUri("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
block:
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?parameters".parseUri().isAbsolute == true
echo("All good!")
echo("All good!")

View File

@@ -456,6 +456,13 @@ type
WriteIOEffect* = object of IOEffect ## Effect describing a write 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 ## \
## Base exception class.
##
@@ -468,7 +475,10 @@ type
msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message
## is bad style.
trace: string
when defined(js):
trace: string
else:
trace: seq[StackTraceEntry]
up: ref Exception # used for stacking exceptions. Not exported!
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: int32): int32 {.magic: "DivI", noSideEffect.}
## computes the integer division. This is roughly the same as
## ``floor(x/y)``.
## ``trunc(x/y)``.
##
## .. code-block:: Nim
## 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.}
## computes the integer division. This is roughly the same as
## ``floor(x/y)``.
## ``trunc(x/y)``.
##
## .. code-block:: Nim
## (7 div 5) == 1
@@ -2015,19 +2025,19 @@ when defined(nimNewRoof):
yield res
inc(res)
iterator `..`*(a, b: int64): int64 {.inline.} =
## A special version of ``..`` for ``int64`` only.
var res = a
while res <= b:
yield res
inc(res)
template dotdotImpl(t) {.dirty.} =
iterator `..`*(a, b: t): t {.inline.} =
## A type specialized version of ``..`` for convenience so that
## mixing integer types work better.
var res = a
while res <= b:
yield res
inc(res)
iterator `..`*(a, b: int32): int32 {.inline.} =
## A special version of ``..`` for ``int32`` only.
var res = a
while res <= b:
yield res
inc(res)
dotdotImpl(int64)
dotdotImpl(int32)
dotdotImpl(uint64)
dotdotImpl(uint32)
else:
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
## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
else:
proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
benign, sideEffect.}
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].}
template newException*(exceptn: typedesc, message: string;
@@ -3706,7 +3716,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
## .. code-block:: nim
## import strutils
##
## template testException(exception, code: expr): stmt =
## template testException(exception, code: untyped): typed =
## try:
## let pos = instantiationInfo()
## discard(code)
@@ -3844,11 +3854,11 @@ type
{.deprecated: [PNimrodNode: NimNode].}
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
## optionally, the block can return an AST tree that will replace the
## eval expression
macro payload: stmt {.gensym.} = blk
macro payload: typed {.gensym.} = blk
payload()
when hasAlloc:

View File

@@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = memLoc
when defined(vcc):
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: "_InterlockedCompareExchange64", header: "<intrin.h>".}
when defined(cpp):
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
{.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
{.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", 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 =
when sizeof(T) == 8:

View File

@@ -370,7 +370,7 @@ proc commandPrompt() =
if dbgUser.len == 0: dbgUser.len = oldLen
# now look what we have to do:
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":
dbgState = dbStepInto
again = false

View File

@@ -155,6 +155,52 @@ when not hasThreadSupport:
var
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) =
when hasThreadSupport:
var
@@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
if tempFrames[j] == nil:
add(s, "(")
add(s, $skipped)
add(s, " calls omitted) ...")
add(s, " calls omitted) ...\n")
else:
var oldLen = s.len
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")
addFrameEntry(s, tempFrames[j])
proc stackTraceAvailable*(): bool
@@ -221,6 +259,13 @@ when hasSomeStackTrace:
auxWriteStackTraceWithBacktrace(s)
else:
add(s, "No stack traceback available\n")
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
when NimStackTrace:
auxWriteStackTrace(framePtr, s)
else:
s = nil
proc stackTraceAvailable(): bool =
when NimStackTrace:
if framePtr == nil:
@@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} =
else:
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) {.
nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception.
@@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) =
when hasSomeStackTrace:
var buf = newStringOfCap(2000)
if isNil(e.trace): rawWriteStackTrace(buf)
else: add(buf, e.trace)
else: add(buf, $e.trace)
add(buf, "Error: unhandled exception: ")
if not isNil(e.msg): add(buf, e.msg)
add(buf, " [")
@@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename
when hasSomeStackTrace:
if e.trace.isNil:
e.trace = ""
rawWriteStackTrace(e.trace)
elif framePtr != nil:
e.trace.add "[[reraised from:\n"
e.trace.add reraisedFrom(reraisedFromBegin)
auxWriteStackTrace(framePtr, e.trace)
e.trace.add "]]\n"
e.trace.add reraisedFrom(reraisedFromEnd)
raiseExceptionAux(e)
proc reraiseException() {.compilerRtl.} =
@@ -349,10 +387,15 @@ proc getStackTrace(): string =
proc getStackTrace(e: ref Exception): string =
if not isNil(e) and not isNil(e.trace):
result = e.trace
result = $e.trace
else:
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):
proc stackOverflow() {.noinline.} =
writeStackTrace()

View File

@@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
when BitsPerPage mod (sizeof(int)*8) != 0:
{.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) =
c.refcount = c.refcount and not colorMask or col

View File

@@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
## it is not necessary to call this directly; use 'spawnX' instead.
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
## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
## 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
## call in the calling thread. Usually it is advised to
## use 'spawn' in order to not block the producer for an unknown

View File

@@ -322,37 +322,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
result.add x
proc add*(result: var string; x: float) =
var buf: array[0..64, char]
when defined(nimNoArrayToCstringConversion):
var n: int = c_sprintf(addr buf, "%.16g", x)
when nimvm:
result.add $x
else:
var n: int = c_sprintf(buf, "%.16g", x)
var hasDot = false
for i in 0..n-1:
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"
var buf: array[0..64, char]
when defined(nimNoArrayToCstringConversion):
var n: int = c_sprintf(addr buf, "%.16g", x)
else:
result.add "inf"
else:
var i = 0
while buf[i] != '\0':
result.add buf[i]
inc i
var n: int = c_sprintf(buf, "%.16g", x)
var hasDot = false
for i in 0..n-1:
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:
result.add "inf"
else:
var i = 0
while buf[i] != '\0':
result.add buf[i]
inc i
proc nimFloatToStr(f: float): string {.compilerproc.} =
result = newStringOfCap(8)

View File

@@ -40,8 +40,7 @@ proc testVarargs(x, y, z: int): seq[int] =
result = waitFor all(a, b, c)
suite "tasyncall":
test "testFuturesWithValue":
block:
let
startTime = cpuTime()
results = testFuturesWithValue(42)
@@ -51,14 +50,14 @@ suite "tasyncall":
doAssert execTime * 1000 < taskCount * sleepDuration
doAssert results == expected
test "testFuturesWithoutValues":
block:
let startTime = cpuTime()
testFuturesWithoutValues()
let execTime = cpuTime() - startTime
doAssert execTime * 1000 < taskCount * sleepDuration
test "testVarargs":
block:
let
startTime = cpuTime()
results = testVarargs(1, 2, 3)
@@ -68,7 +67,7 @@ suite "tasyncall":
doAssert execTime * 100 < taskCount * sleepDuration
doAssert results == expected
test "all on seq[Future]":
block:
let
noIntFuturesFut = all(newSeq[Future[int]]())
noVoidFuturesFut = all(newSeq[Future[void]]())

View File

@@ -3,11 +3,22 @@ discard """
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.} =
let s = newAsyncNativeSocket()
await s.connect("example.com", 80.Port)
await s.connect("localhost", 10335.Port)
await s.send("123")
echo "Finished"

View File

@@ -86,7 +86,7 @@ proc readMessages(server: AsyncFD) {.async.} =
size = 0
var grammString = $cstring(addr buffer)
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),
cast[ptr SockAddr](addr saddr), slen)
inc(msgCount)

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

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: "hello"
"""

10
tests/cpp/tcasts.nim Normal file
View 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')

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: '''
cat
cat

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: '''foo
bar
Need odd and >= 3 digits##

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: ''''''
disabled: true
"""

View File

@@ -1,4 +1,5 @@
discard """
targets: "cpp"
cmd: "nim cpp --threads:on $file"
"""

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: '''6.0'''
"""

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: '''int
float'''
"""

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: '''Hello world'''
"""

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
"""
{.emit: """

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
"""
import nativesockets

View File

@@ -1,3 +1,6 @@
discard """
targets: "cpp"
"""
# bug #2259
type Mat4f* = array[0..15, float]

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
"""
import tables, lists

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
output: "42"
"""

View File

@@ -1,3 +1,7 @@
discard """
targets: "cpp"
"""
type
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object

View File

@@ -1,4 +1,5 @@
discard """
targets: "cpp"
cmd: "nim cpp --hints:on --threads:on $options $file"
"""

View File

@@ -1,6 +1,6 @@
discard """
targets: "cpp"
output: '''100'''
cmd: "nim cpp $file"
"""
import typeinfo

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
"""
# bug #2841
import typeinfo

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $file"
targets: "cpp"
"""
{.emit: """

View File

@@ -1,7 +1,7 @@
discard """
targets: "cpp"
output: '''(x: 1.0)
(x: 0.0)'''
cmd: "nim cpp $file"
disabled: "true"
"""

View 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

View File

@@ -5,7 +5,8 @@ discard """
6
abcdefghijklmnopqrstuvwxyz
145 23
3'''
3
2'''
"""
import strutils
@@ -122,3 +123,21 @@ var testTry =
PFooBase(field: 5)
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
)

View File

@@ -1,5 +1,6 @@
discard """
exitcode: 0
disabled: '''true'''
"""
import moverloading_typedesc
import tables

View File

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

View 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