mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 20:04:18 +00:00
Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -57,3 +57,7 @@ dist/
|
||||
# Private directories and files (IDEs)
|
||||
.*/
|
||||
~*
|
||||
|
||||
# testament cruft
|
||||
testresults/
|
||||
test.txt
|
||||
|
||||
30
changelog.md
30
changelog.md
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
|
||||
@@ -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("//", "")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)+\"\")"],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -162,6 +162,7 @@ const
|
||||
RodExt* = "rod"
|
||||
HtmlExt* = "html"
|
||||
JsonExt* = "json"
|
||||
TagsExt* = "tags"
|
||||
TexExt* = "tex"
|
||||
IniExt* = "ini"
|
||||
DefaultConfig* = "nim.cfg"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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, ", ")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
--------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
17
doc/tut2.rst
17
doc/tut2.rst
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
4
koch.nim
4
koch.nim
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -197,4 +197,4 @@ macro dump*(x: typed): untyped =
|
||||
let s = x.toStrLit
|
||||
let r = quote do:
|
||||
debugEcho `s`, " = ", `x`
|
||||
return r
|
||||
return r
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``gentabs`` module implements an efficient hash table that is a
|
||||
## key-value mapping. The keys are required to be strings, but the values
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||
##
|
||||
## **Warning:** This module is deprecated, new code shouldn't use it!
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
import
|
||||
os, hashes, strutils
|
||||
|
||||
type
|
||||
GenTableMode* = enum ## describes the table's key matching mode
|
||||
modeCaseSensitive, ## case sensitive matching of keys
|
||||
modeCaseInsensitive, ## case insensitive matching of keys
|
||||
modeStyleInsensitive ## style sensitive matching of keys
|
||||
|
||||
GenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
|
||||
GenTable*[T] = object of RootObj
|
||||
counter: int
|
||||
data: GenKeyValuePairSeq[T]
|
||||
mode: GenTableMode
|
||||
|
||||
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
|
||||
|
||||
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
|
||||
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
startSize = 64
|
||||
|
||||
|
||||
proc len*[T](tbl: PGenTable[T]): int {.inline.} =
|
||||
## returns the number of keys in `tbl`.
|
||||
result = tbl.counter
|
||||
|
||||
iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
|
||||
## iterates over any (key, value) pair in the table `tbl`.
|
||||
for h in 0..high(tbl.data):
|
||||
if not isNil(tbl.data[h].key):
|
||||
yield (tbl.data[h].key, tbl.data[h].val)
|
||||
|
||||
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
|
||||
case tbl.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
||||
of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
|
||||
|
||||
proc myCmp[T](tbl: PGenTable[T], a, b: string): bool =
|
||||
case tbl.mode
|
||||
of modeCaseSensitive: result = cmp(a, b) == 0
|
||||
of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
|
||||
of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
|
||||
|
||||
proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
|
||||
## creates a new generic hash table that is empty.
|
||||
new(result)
|
||||
result.mode = mode
|
||||
result.counter = 0
|
||||
newSeq(result.data, startSize)
|
||||
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
|
||||
while not isNil(tbl.data[h].key):
|
||||
if myCmp(tbl, tbl.data[h].key, key):
|
||||
return h
|
||||
h = nextTry(h, high(tbl.data))
|
||||
result = - 1
|
||||
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
|
||||
key: string, val: T) =
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc enlarge[T](tbl: PGenTable[T]) =
|
||||
var n: GenKeyValuePairSeq[T]
|
||||
newSeq(n, len(tbl.data) * growthFactor)
|
||||
for i in countup(0, high(tbl.data)):
|
||||
if not isNil(tbl.data[i].key):
|
||||
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
|
||||
swap(tbl.data, n)
|
||||
|
||||
proc hasKey*[T](tbl: PGenTable[T], key: string): bool =
|
||||
## returns true iff `key` is in the table `tbl`.
|
||||
result = rawGet(tbl, key) >= 0
|
||||
|
||||
proc `[]`*[T](tbl: PGenTable[T], key: string): T =
|
||||
## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`,
|
||||
## default(T) is returned and no exception is raised. One can check
|
||||
## with ``hasKey`` whether the key exists.
|
||||
var index = rawGet(tbl, key)
|
||||
if index >= 0: result = tbl.data[index].val
|
||||
|
||||
proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) =
|
||||
## puts a (key, value)-pair into `tbl`.
|
||||
var index = rawGet(tbl, key)
|
||||
if index >= 0:
|
||||
tbl.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl)
|
||||
rawInsert(tbl, tbl.data, key, val)
|
||||
inc(tbl.counter)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
#
|
||||
# Verify tables of integer values (string keys)
|
||||
#
|
||||
var x = newGenTable[int](modeCaseInsensitive)
|
||||
x["one"] = 1
|
||||
x["two"] = 2
|
||||
x["three"] = 3
|
||||
x["four"] = 4
|
||||
x["five"] = 5
|
||||
assert(len(x) == 5) # length procedure works
|
||||
assert(x["one"] == 1) # case-sensitive lookup works
|
||||
assert(x["ONE"] == 1) # case-insensitive should work for this table
|
||||
assert(x["one"]+x["two"] == 3) # make sure we're getting back ints
|
||||
assert(x.hasKey("one")) # hasKey should return 'true' for a key
|
||||
# of "one"...
|
||||
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
||||
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
||||
assert(x[k]==v)
|
||||
|
||||
#
|
||||
# Verify a table of user-defined types
|
||||
#
|
||||
type
|
||||
MyType = tuple[first, second: string] # a pair of strings
|
||||
{.deprecated: [TMyType: MyType].}
|
||||
|
||||
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
|
||||
# value is MyType tuple
|
||||
|
||||
#var junk: MyType = ("OK", "Here")
|
||||
|
||||
#echo junk.first, " ", junk.second
|
||||
|
||||
y["Hello"] = ("Hello", "World")
|
||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||
#y["Hello"] = MyType( ("Hello", "World") )
|
||||
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
|
||||
|
||||
assert( not isNil(y["Hello"].first) )
|
||||
assert( y["Hello"].first == "Hello" )
|
||||
assert( y["Hello"].second == "World" )
|
||||
|
||||
#
|
||||
# Verify table of tables
|
||||
#
|
||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||
# a hash table of ints
|
||||
|
||||
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
||||
z["first"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["first"]["one"] = 1
|
||||
z["first"]["two"] = 2
|
||||
z["first"]["three"] = 3
|
||||
|
||||
z["second"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["second"]["red"] = 10
|
||||
z["second"]["blue"] = 20
|
||||
|
||||
assert(len(z) == 2) # length of outer table
|
||||
assert(len(z["first"]) == 3) # length of "first" table
|
||||
assert(len(z["second"]) == 2) # length of "second" table
|
||||
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
||||
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
||||
|
||||
when false:
|
||||
# disabled: depends on hash order:
|
||||
var output = ""
|
||||
for k, v in pairs(z):
|
||||
output.add( "$# ($#) ->\L" % [k,$len(v)] )
|
||||
for k2,v2 in pairs(v):
|
||||
output.add( " $# <-> $#\L" % [k2,$v2] )
|
||||
|
||||
let expected = unindent """
|
||||
first (3) ->
|
||||
two <-> 2
|
||||
three <-> 3
|
||||
one <-> 1
|
||||
second (2) ->
|
||||
red <-> 10
|
||||
blue <-> 20
|
||||
"""
|
||||
assert output == expected
|
||||
@@ -43,6 +43,7 @@ const bsdPlatform = defined(macosx) or defined(freebsd) or
|
||||
defined(dragonfly)
|
||||
|
||||
when defined(nimdoc):
|
||||
type SocketHandle = int
|
||||
type
|
||||
Selector*[T] = ref object
|
||||
## An object which holds descriptors to be checked for read/write status
|
||||
|
||||
@@ -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 `$`
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
type OneVarFunction* = proc (x: float): float
|
||||
|
||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||
|
||||
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
tuple[rootx, rooty: float, success: bool]=
|
||||
## Searches `function` for a root between `xmin` and `xmax`
|
||||
## using brents method. If the function value at `xmin`and `xmax` has the
|
||||
## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
|
||||
## and succes is set to false.
|
||||
## Otherwise there exists at least one root and success is set to true.
|
||||
## This root is searched for at most `maxiter` iterations.
|
||||
## If `tol` tolerance is reached within `maxiter` iterations
|
||||
## the root refinement stops and success=true.
|
||||
|
||||
# see http://en.wikipedia.org/wiki/Brent%27s_method
|
||||
var
|
||||
a=xmin
|
||||
b=xmax
|
||||
c=a
|
||||
d=1.0e308
|
||||
fa=function(a)
|
||||
fb=function(b)
|
||||
fc=fa
|
||||
s=0.0
|
||||
fs=0.0
|
||||
mflag:bool
|
||||
i=0
|
||||
tmp2:float
|
||||
|
||||
if fa*fb>=0:
|
||||
if abs(fa)<abs(fb):
|
||||
return (a,fa,false)
|
||||
else:
|
||||
return (b,fb,false)
|
||||
|
||||
if abs(fa)<abs(fb):
|
||||
swap(fa,fb)
|
||||
swap(a,b)
|
||||
|
||||
while fb!=0.0 and abs(a-b)>tol:
|
||||
if fa!=fc and fb!=fc: # inverse quadratic interpolation
|
||||
s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
|
||||
else: #secant rule
|
||||
s = b - fb * (b - a) / (fb - fa)
|
||||
tmp2 = (3.0 * a + b) / 4.0
|
||||
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
|
||||
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
|
||||
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
|
||||
s=(a+b)/2.0
|
||||
mflag=true
|
||||
else:
|
||||
if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
|
||||
s=(a+b)/2.0
|
||||
mflag=true
|
||||
else:
|
||||
mflag=false
|
||||
fs = function(s)
|
||||
d = c
|
||||
c = b
|
||||
fc = fb
|
||||
if fa * fs<0.0:
|
||||
b=s
|
||||
fb=fs
|
||||
else:
|
||||
a=s
|
||||
fa=fs
|
||||
if abs(fa)<abs(fb):
|
||||
swap(a,b)
|
||||
swap(fa,fb)
|
||||
inc i
|
||||
if i>maxiter:
|
||||
break
|
||||
|
||||
return (b,fb,true)
|
||||
@@ -1195,14 +1195,15 @@ when defined(nimdoc):
|
||||
## Returns the number of `command line arguments`:idx: given to the
|
||||
## 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.}
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
import math
|
||||
import strutils
|
||||
import numeric
|
||||
|
||||
type
|
||||
Poly* = object
|
||||
cofs:seq[float]
|
||||
|
||||
{.deprecated: [TPoly: Poly].}
|
||||
|
||||
proc degree*(p:Poly):int=
|
||||
## Returns the degree of the polynomial,
|
||||
## that is the number of coefficients-1
|
||||
return p.cofs.len-1
|
||||
|
||||
|
||||
proc eval*(p:Poly,x:float):float=
|
||||
## Evaluates a polynomial function value for `x`
|
||||
## quickly using Horners method
|
||||
var n=p.degree
|
||||
result=p.cofs[n]
|
||||
dec n
|
||||
while n>=0:
|
||||
result = result*x+p.cofs[n]
|
||||
dec n
|
||||
|
||||
proc `[]` *(p:Poly;idx:int):float=
|
||||
## Gets a coefficient of the polynomial.
|
||||
## p[2] will returns the quadric term, p[3] the cubic etc.
|
||||
## Out of bounds index will return 0.0.
|
||||
if idx<0 or idx>p.degree:
|
||||
return 0.0
|
||||
return p.cofs[idx]
|
||||
|
||||
proc `[]=` *(p:var Poly;idx:int,v:float)=
|
||||
## Sets an coefficient of the polynomial by index.
|
||||
## p[2] set the quadric term, p[3] the cubic etc.
|
||||
## If index is out of range for the coefficients,
|
||||
## the polynomial grows to the smallest needed degree.
|
||||
assert(idx>=0)
|
||||
|
||||
if idx>p.degree: #polynomial must grow
|
||||
var oldlen=p.cofs.len
|
||||
p.cofs.setLen(idx+1)
|
||||
for q in oldlen.. <high(p.cofs):
|
||||
p.cofs[q]=0.0 #new-grown coefficients set to zero
|
||||
|
||||
p.cofs[idx]=v
|
||||
|
||||
|
||||
iterator items*(p:Poly):float=
|
||||
## Iterates through the coefficients of the polynomial.
|
||||
var i=p.degree
|
||||
while i>=0:
|
||||
yield p[i]
|
||||
dec i
|
||||
|
||||
proc clean*(p:var Poly;zerotol=0.0)=
|
||||
## Removes leading zero coefficients of the polynomial.
|
||||
## An optional tolerance can be given for what's considered zero.
|
||||
var n=p.degree
|
||||
var relen=false
|
||||
|
||||
while n>0 and abs(p[n])<=zerotol: # >0 => keep at least one coefficient
|
||||
dec n
|
||||
relen=true
|
||||
|
||||
if relen: p.cofs.setLen(n+1)
|
||||
|
||||
|
||||
proc `$` *(p:Poly):string =
|
||||
## Gets a somewhat reasonable string representation of the polynomial
|
||||
## The format should be compatible with most online function plotters,
|
||||
## for example directly in google search
|
||||
result=""
|
||||
var first=true #might skip + sign if first coefficient
|
||||
|
||||
for idx in countdown(p.degree,0):
|
||||
let a=p[idx]
|
||||
|
||||
if a==0.0:
|
||||
continue
|
||||
|
||||
if a>= 0.0 and not first:
|
||||
result.add('+')
|
||||
first=false
|
||||
|
||||
if a!=1.0 or idx==0:
|
||||
result.add(formatFloat(a,ffDefault,0))
|
||||
if idx>=2:
|
||||
result.add("x^" & $idx)
|
||||
elif idx==1:
|
||||
result.add("x")
|
||||
|
||||
if result=="":
|
||||
result="0"
|
||||
|
||||
|
||||
proc derivative*(p: Poly): Poly=
|
||||
## Returns a new polynomial, which is the derivative of `p`
|
||||
newSeq[float](result.cofs,p.degree)
|
||||
for idx in 0..high(result.cofs):
|
||||
result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
|
||||
|
||||
proc diff*(p:Poly,x:float):float=
|
||||
## Evaluates the differentiation of a polynomial with
|
||||
## respect to `x` quickly using a modifed Horners method
|
||||
var n=p.degree
|
||||
result=p[n]*float(n)
|
||||
dec n
|
||||
while n>=1:
|
||||
result = result*x+p[n]*float(n)
|
||||
dec n
|
||||
|
||||
proc integral*(p:Poly):Poly=
|
||||
## Returns a new polynomial which is the indefinite
|
||||
## integral of `p`. The constant term is set to 0.0
|
||||
newSeq(result.cofs,p.cofs.len+1)
|
||||
result.cofs[0]=0.0 #constant arbitrary term, use 0.0
|
||||
for i in 1..high(result.cofs):
|
||||
result.cofs[i]=p.cofs[i-1]/float(i)
|
||||
|
||||
|
||||
proc integrate*(p:Poly;xmin,xmax:float):float=
|
||||
## Computes the definite integral of `p` between `xmin` and `xmax`
|
||||
## quickly using a modified version of Horners method
|
||||
var
|
||||
n=p.degree
|
||||
s1=p[n]/float(n+1)
|
||||
s2=s1
|
||||
fac:float
|
||||
|
||||
dec n
|
||||
while n>=0:
|
||||
fac=p[n]/float(n+1)
|
||||
s1 = s1*xmin+fac
|
||||
s2 = s2*xmax+fac
|
||||
dec n
|
||||
|
||||
result=s2*xmax-s1*xmin
|
||||
|
||||
proc initPoly*(cofs:varargs[float]):Poly=
|
||||
## Initializes a polynomial with given coefficients.
|
||||
## The most significant coefficient is first, so to create x^2-2x+3:
|
||||
## intiPoly(1.0,-2.0,3.0)
|
||||
if len(cofs)<=0:
|
||||
result.cofs= @[0.0] #need at least one coefficient
|
||||
else:
|
||||
# reverse order of coefficients so indexing matches degree of
|
||||
# coefficient...
|
||||
result.cofs= @[]
|
||||
for idx in countdown(cofs.len-1,0):
|
||||
result.cofs.add(cofs[idx])
|
||||
|
||||
result.clean #remove leading zero terms
|
||||
|
||||
|
||||
proc divMod*(p,d:Poly;q,r:var Poly)=
|
||||
## Divides `p` with `d`, and stores the quotinent in `q` and
|
||||
## the remainder in `d`
|
||||
var
|
||||
pdeg=p.degree
|
||||
ddeg=d.degree
|
||||
power=p.degree-d.degree
|
||||
ratio:float
|
||||
|
||||
r.cofs = p.cofs #initial remainder=numerator
|
||||
if power<0: #denominator is larger than numerator
|
||||
q.cofs= @ [0.0] #quotinent is 0.0
|
||||
return # keep remainder as numerator
|
||||
|
||||
q.cofs=newSeq[float](power+1)
|
||||
|
||||
for i in countdown(pdeg,ddeg):
|
||||
ratio=r.cofs[i]/d.cofs[ddeg]
|
||||
|
||||
q.cofs[i-ddeg]=ratio
|
||||
r.cofs[i]=0.0
|
||||
|
||||
for j in countup(0,<ddeg):
|
||||
var idx=i-ddeg+j
|
||||
r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
|
||||
|
||||
r.clean # drop zero coefficients in remainder
|
||||
|
||||
proc `+` *(p1:Poly,p2:Poly):Poly=
|
||||
## Adds two polynomials
|
||||
var n=max(p1.cofs.len,p2.cofs.len)
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
for idx in countup(0,n-1):
|
||||
result[idx]=p1[idx]+p2[idx]
|
||||
|
||||
result.clean # drop zero coefficients in remainder
|
||||
|
||||
proc `*` *(p1:Poly,p2:Poly):Poly=
|
||||
## Multiplies the polynomial `p1` with `p2`
|
||||
var
|
||||
d1=p1.degree
|
||||
d2=p2.degree
|
||||
n=d1+d2
|
||||
idx:int
|
||||
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
for i1 in countup(0,d1):
|
||||
for i2 in countup(0,d2):
|
||||
idx=i1+i2
|
||||
result[idx]=result[idx]+p1[i1]*p2[i2]
|
||||
|
||||
result.clean
|
||||
|
||||
proc `*` *(p:Poly,f:float):Poly=
|
||||
## Multiplies the polynomial `p` with a real number
|
||||
newSeq(result.cofs,p.cofs.len)
|
||||
for i in 0..high(p.cofs):
|
||||
result[i]=p.cofs[i]*f
|
||||
result.clean
|
||||
|
||||
proc `*` *(f:float,p:Poly):Poly=
|
||||
## Multiplies a real number with a polynomial
|
||||
return p*f
|
||||
|
||||
proc `-`*(p:Poly):Poly=
|
||||
## Negates a polynomial
|
||||
result=p
|
||||
for i in countup(0,<result.cofs.len):
|
||||
result.cofs[i]= -result.cofs[i]
|
||||
|
||||
proc `-` *(p1:Poly,p2:Poly):Poly=
|
||||
## Subtract `p1` with `p2`
|
||||
var n=max(p1.cofs.len,p2.cofs.len)
|
||||
newSeq(result.cofs,n)
|
||||
|
||||
for idx in countup(0,n-1):
|
||||
result[idx]=p1[idx]-p2[idx]
|
||||
|
||||
result.clean # drop zero coefficients in remainder
|
||||
|
||||
proc `/`*(p:Poly,f:float):Poly=
|
||||
## Divides polynomial `p` with a real number `f`
|
||||
newSeq(result.cofs,p.cofs.len)
|
||||
for i in 0..high(p.cofs):
|
||||
result[i]=p.cofs[i]/f
|
||||
result.clean
|
||||
|
||||
proc `/` *(p,q:Poly):Poly=
|
||||
## Divides polynomial `p` with polynomial `q`
|
||||
var dummy:Poly
|
||||
p.divMod(q,result,dummy)
|
||||
|
||||
proc `mod` *(p,q:Poly):Poly=
|
||||
## Computes the polynomial modulo operation,
|
||||
## that is the remainder of `p`/`q`
|
||||
var dummy:Poly
|
||||
p.divMod(q,dummy,result)
|
||||
|
||||
|
||||
proc normalize*(p:var Poly)=
|
||||
## Multiplies the polynomial inplace by a term so that
|
||||
## the leading term is 1.0.
|
||||
## This might lead to an unstable polynomial
|
||||
## if the leading term is zero.
|
||||
p=p/p[p.degree]
|
||||
|
||||
|
||||
proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
|
||||
## Solves the quadric equation `ax^2+bx+c`, with a possible
|
||||
## tolerance `zerotol` to find roots of curves just 'touching'
|
||||
## the x axis. Returns sequence with 0,1 or 2 solutions.
|
||||
|
||||
var p,q,d:float
|
||||
|
||||
p=b/(2.0*a)
|
||||
|
||||
if p==Inf or p==NegInf: #linear equation..
|
||||
var linrt= -c/b
|
||||
if linrt==Inf or linrt==NegInf: #constant only
|
||||
return @[]
|
||||
return @[linrt]
|
||||
|
||||
q=c/a
|
||||
d=p*p-q
|
||||
|
||||
if d<0.0:
|
||||
#check for inside zerotol range for neg. roots
|
||||
var err=a*p*p-b*p+c #evaluate error at parabola center axis
|
||||
if(err<=zerotol): return @[-p]
|
||||
return @[]
|
||||
else:
|
||||
var sr=sqrt(d)
|
||||
result= @[-sr-p,sr-p]
|
||||
|
||||
proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
|
||||
## helper function for `roots` function
|
||||
## quickly computes a range, guaranteed to contain
|
||||
## all the real roots of the polynomial
|
||||
# see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
|
||||
|
||||
var deg=p.degree
|
||||
var d=p[deg]
|
||||
var bound1,bound2:float
|
||||
|
||||
for i in countup(0,deg):
|
||||
var c=abs(p.cofs[i]/d)
|
||||
bound1=max(bound1,c+1.0)
|
||||
bound2=bound2+c
|
||||
|
||||
bound2=max(1.0,bound2)
|
||||
result.xmax=min(bound1,bound2)
|
||||
result.xmin= -result.xmax
|
||||
|
||||
|
||||
proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
|
||||
## helper function for `roots` function
|
||||
## try to do a numeric search for a single root in range xp0-xp1,
|
||||
## adding it to `res` (allocating `res` if nil)
|
||||
var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
|
||||
if br.success:
|
||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
||||
res.add(br.rootx)
|
||||
else:
|
||||
#this might be a 'touching' case, check function value against
|
||||
#zero tolerance
|
||||
if abs(br.rooty)<=zerotol:
|
||||
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
|
||||
res.add(br.rootx)
|
||||
|
||||
|
||||
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
|
||||
## Computes the real roots of the polynomial `p`
|
||||
## `tol` is the tolerance used to break searching for each root when reached.
|
||||
## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
|
||||
## and is used to find roots for curves that only 'touch' the x-axis.
|
||||
## `mergetol` is the tolerance, of which two x-values are considered being the same root.
|
||||
## `maxiter` can be used to limit the number of iterations for each root.
|
||||
## Returns a (possibly empty) sorted sequence with the solutions.
|
||||
var deg=p.degree
|
||||
if deg<=0: #constant only => no roots
|
||||
return @[]
|
||||
elif p.degree==1: #linear
|
||||
var linrt= -p.cofs[0]/p.cofs[1]
|
||||
if linrt==Inf or linrt==NegInf:
|
||||
return @[] #constant only => no roots
|
||||
return @[linrt]
|
||||
elif p.degree==2:
|
||||
return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
|
||||
else:
|
||||
# degree >=3 , find min/max points of polynomial with recursive
|
||||
# derivative and do a numerical search for root between each min/max
|
||||
var range=p.getRangeForRoots()
|
||||
var minmax=p.derivative.roots(tol,zerotol,mergetol)
|
||||
result= @[]
|
||||
if minmax!=nil: #ie. we have minimas/maximas in this function
|
||||
for x in minmax.items:
|
||||
addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
|
||||
range.xmin=x
|
||||
addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2011 Philippe Lhoste
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Module for converting an integer to a Roman numeral.
|
||||
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
||||
##
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
const
|
||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
||||
'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
|
||||
|
||||
proc romanToDecimal*(romanVal: string): int =
|
||||
## Converts a Roman numeral to its int representation.
|
||||
result = 0
|
||||
var prevVal = 0
|
||||
for i in countdown(romanVal.len - 1, 0):
|
||||
var val = 0
|
||||
case romanVal[i]
|
||||
of 'I', 'i': val = 1
|
||||
of 'V', 'v': val = 5
|
||||
of 'X', 'x': val = 10
|
||||
of 'L', 'l': val = 50
|
||||
of 'C', 'c': val = 100
|
||||
of 'D', 'd': val = 500
|
||||
of 'M', 'm': val = 1000
|
||||
else:
|
||||
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
|
||||
if val >= prevVal:
|
||||
inc(result, val)
|
||||
else:
|
||||
dec(result, val)
|
||||
prevVal = val
|
||||
|
||||
proc decimalToRoman*(number: range[1..3_999]): string =
|
||||
## Converts a number to a Roman numeral.
|
||||
const romanComposites = [
|
||||
("M", 1000), ("CM", 900),
|
||||
("D", 500), ("CD", 400), ("C", 100),
|
||||
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
|
||||
("V", 5), ("IV", 4), ("I", 1)]
|
||||
result = ""
|
||||
var decVal: int = number
|
||||
for key, val in items(romanComposites):
|
||||
while decVal >= val:
|
||||
dec(decVal, val)
|
||||
result.add(key)
|
||||
|
||||
when isMainModule:
|
||||
for i in 1 .. 3_999:
|
||||
assert i == i.decimalToRoman.romanToDecimal
|
||||
|
||||
@@ -1927,7 +1927,7 @@ type
|
||||
{.deprecated: [TFloatFormat: FloatFormatMode].}
|
||||
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]]())
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
33
tests/ccgbugs/trefseqsort.nim
Normal file
33
tests/ccgbugs/trefseqsort.nim
Normal file
@@ -0,0 +1,33 @@
|
||||
discard """
|
||||
output: '''@[0, 4, 9, 1, 3, 2]
|
||||
@[0, 1, 2, 3, 9]'''
|
||||
"""
|
||||
# bug #6724
|
||||
import algorithm
|
||||
|
||||
type
|
||||
Bar = object
|
||||
bar: ref seq[int]
|
||||
Foo = ref Bar
|
||||
|
||||
proc test(x: ref Foo) =
|
||||
x.bar[].del(1)
|
||||
x.bar[].sort(cmp)
|
||||
|
||||
proc main() =
|
||||
var foo: ref Foo
|
||||
new(foo)
|
||||
|
||||
var s = @[0, 4, 9, 1, 3, 2]
|
||||
|
||||
var sr: ref seq[int]
|
||||
new(sr)
|
||||
sr[] = s
|
||||
|
||||
foo[] = Foo(bar: sr)
|
||||
echo($foo.bar[])
|
||||
|
||||
test(foo)
|
||||
echo($foo.bar[])
|
||||
|
||||
main()
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: "hello"
|
||||
"""
|
||||
|
||||
|
||||
10
tests/cpp/tcasts.nim
Normal file
10
tests/cpp/tcasts.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
output: ""
|
||||
"""
|
||||
|
||||
block: #5979
|
||||
var a = 'a'
|
||||
var p: pointer = cast[pointer](a)
|
||||
var c = cast[char](p)
|
||||
doAssert(c == 'a')
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: '''
|
||||
cat
|
||||
cat
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: '''foo
|
||||
bar
|
||||
Need odd and >= 3 digits##
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: ''''''
|
||||
disabled: true
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
cmd: "nim cpp --threads:on $file"
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: '''6.0'''
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: '''int
|
||||
float'''
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: '''Hello world'''
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
{.emit: """
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
import nativesockets
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
# bug #2259
|
||||
type Mat4f* = array[0..15, float]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
import tables, lists
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
output: "42"
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
type
|
||||
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
cmd: "nim cpp --hints:on --threads:on $options $file"
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
output: '''100'''
|
||||
cmd: "nim cpp $file"
|
||||
"""
|
||||
|
||||
import typeinfo
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
"""
|
||||
# bug #2841
|
||||
import typeinfo
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
targets: "cpp"
|
||||
"""
|
||||
|
||||
{.emit: """
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
output: '''(x: 1.0)
|
||||
(x: 0.0)'''
|
||||
cmd: "nim cpp $file"
|
||||
disabled: "true"
|
||||
"""
|
||||
|
||||
|
||||
23
tests/errmsgs/tmake_tuple_visible.nim
Normal file
23
tests/errmsgs/tmake_tuple_visible.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
discard """
|
||||
errormsg: '''got (tuple of (type NimEdAppWindow, int))'''
|
||||
line: 22
|
||||
nimout: '''got (tuple of (type NimEdAppWindow, int))
|
||||
but expected one of:
|
||||
template xxx(tn: typedesc; i: int)'''
|
||||
"""
|
||||
|
||||
type
|
||||
NimEdAppWindow = ptr NimEdAppWindowObj
|
||||
NimEdAppWindowObj = object
|
||||
i: int
|
||||
|
||||
template gDefineTypeExtended*(tn: typeDesc) =
|
||||
discard
|
||||
|
||||
gDefineTypeExtended (NimEdAppWindow)
|
||||
|
||||
template xxx*(tn: typeDesc, i: int) =
|
||||
discard
|
||||
|
||||
xxx (NimEdAppWindow, 0)
|
||||
# bug #6776
|
||||
@@ -5,7 +5,8 @@ discard """
|
||||
6
|
||||
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
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
discard """
|
||||
exitcode: 0
|
||||
disabled: '''true'''
|
||||
"""
|
||||
import moverloading_typedesc
|
||||
import tables
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
discard """
|
||||
file: "tromans.nim"
|
||||
output: "success"
|
||||
"""
|
||||
import
|
||||
strutils
|
||||
|
||||
## Convert an integer to a Roman numeral
|
||||
# See http://en.wikipedia.org/wiki/Roman_numerals for reference
|
||||
|
||||
proc raiseInvalidValue(msg: string) {.noreturn.} =
|
||||
# Yes, we really need a shorthand for this code...
|
||||
var e: ref EInvalidValue
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
# I should use a class, perhaps.
|
||||
# --> No. Why introduce additional state into such a simple and nice
|
||||
# interface? State is evil. :D
|
||||
|
||||
proc RomanToDecimal(romanVal: string): int =
|
||||
result = 0
|
||||
var prevVal = 0
|
||||
for i in countdown(romanVal.len - 1, 0):
|
||||
var val = 0
|
||||
case romanVal[i]
|
||||
of 'I', 'i': val = 1
|
||||
of 'V', 'v': val = 5
|
||||
of 'X', 'x': val = 10
|
||||
of 'L', 'l': val = 50
|
||||
of 'C', 'c': val = 100
|
||||
of 'D', 'd': val = 500
|
||||
of 'M', 'm': val = 1000
|
||||
else: raiseInvalidValue("Incorrect character in roman numeral! (" &
|
||||
$romanVal[i] & ")")
|
||||
if val >= prevVal:
|
||||
inc(result, val)
|
||||
else:
|
||||
dec(result, val)
|
||||
prevVal = val
|
||||
|
||||
proc DecimalToRoman(decValParam: int): string =
|
||||
# Apparently numbers cannot be above 4000
|
||||
# Well, they can be (using overbar or parenthesis notation)
|
||||
# but I see little interest (beside coding challenge) in coding them as
|
||||
# we rarely use huge Roman numeral.
|
||||
const romanComposites = [
|
||||
("M", 1000), ("CM", 900),
|
||||
("D", 500), ("CD", 400), ("C", 100),
|
||||
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
|
||||
("V", 5), ("IV", 4), ("I", 1)]
|
||||
if decValParam < 1 or decValParam > 3999:
|
||||
raiseInvalidValue("number not representable")
|
||||
result = ""
|
||||
var decVal = decValParam
|
||||
for key, val in items(romanComposites):
|
||||
while decVal >= val:
|
||||
dec(decVal, val)
|
||||
result.add(key)
|
||||
|
||||
for i in 1..100:
|
||||
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
|
||||
|
||||
for i in items([1238, 1777, 3830, 2401, 379, 33, 940, 3973]):
|
||||
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
|
||||
|
||||
echo "success" #OUT success
|
||||
|
||||
|
||||
|
||||
47
tests/overload/tstatic_with_converter.nim
Normal file
47
tests/overload/tstatic_with_converter.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
output: '''
|
||||
9.0'''
|
||||
"""
|
||||
|
||||
### bug #6773
|
||||
|
||||
{.emit: """ /*INCLUDESECTION*/
|
||||
typedef double cimported;
|
||||
|
||||
cimported set1_imported(double x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
"""}
|
||||
|
||||
type vfloat{.importc: "cimported".} = object
|
||||
|
||||
proc set1(a: float): vfloat {.importc: "set1_imported".}
|
||||
|
||||
converter scalar_to_vector(x: float): vfloat =
|
||||
set1(x)
|
||||
|
||||
proc sqrt(x: vfloat): vfloat =
|
||||
x
|
||||
|
||||
proc pow(x, y: vfloat): vfloat =
|
||||
y
|
||||
|
||||
proc `^`(x: vfloat, exp: static[int]): vfloat =
|
||||
when exp == 0:
|
||||
1.0
|
||||
else:
|
||||
x
|
||||
|
||||
proc `^`(x: vfloat, exp: static[float]): vfloat =
|
||||
when exp == 0.5:
|
||||
sqrt(x)
|
||||
else:
|
||||
pow(x, exp)
|
||||
|
||||
proc `$`(x: vfloat): string =
|
||||
let y = cast[ptr float](unsafeAddr x)
|
||||
echo y[]
|
||||
|
||||
let x = set1(9.0)
|
||||
echo x^0.5
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user