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

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

4
.gitignore vendored
View file

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

View file

@ -2,9 +2,7 @@
### Changes affecting backwards compatibility ### Changes affecting backwards compatibility
- Removed basic2d/basic3d out of the stdlib and into Nimble packages.
These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
or another package.
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to - Arrays of char cannot be converted to ``cstring`` anymore, pointers to
arrays of char can! This means ``$`` for arrays can finally exist arrays of char can! This means ``$`` for arrays can finally exist
in ``system.nim`` and do the right thing. in ``system.nim`` and do the right thing.
@ -16,7 +14,6 @@
module. module.
- The overloading rules changed slightly so that constrained generics are - The overloading rules changed slightly so that constrained generics are
preferred over unconstrained generics. (Bug #6526) preferred over unconstrained generics. (Bug #6526)
- Removed libuv out of the stdlib and into Nimble packages.
- It is now possible to forward declare object types so that mutually - It is now possible to forward declare object types so that mutually
recursive types can be created across module boundaries. See recursive types can be created across module boundaries. See
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects) [package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
@ -40,8 +37,6 @@
- Added ``typetraits.$`` as an alias for ``typetraits.name``. - Added ``typetraits.$`` as an alias for ``typetraits.name``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv`` - ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
what to return if the environment variable does not exist. what to return if the environment variable does not exist.
- Removed PDCurses wrapper from the stdlib and published it as a separate
Nimble package.
- Bodies of ``for`` loops now get their own scope: - Bodies of ``for`` loops now get their own scope:
.. code-block:: nim .. code-block:: nim
@ -74,3 +69,26 @@ This now needs to be written as:
var a = -5 var a = -5
for i in a..b: for i in a..b:
echo i echo i
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
precision digits. The previous ``precision = 0`` behavior (default formatting)
is now available via ``precision = -1``.
- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
the documentation generator. The old version 1 can still be accessed
via the new ``nim doc0`` command.
- Added ``system.getStackTraceEntries`` that allows you to access the stack
trace in a structured manner without string parsing.
- Added ``sequtils.mapLiterals`` for easier construction of array and tuple
literals.
- Added ``parseutils.parseSaturatedNatural``.
- Moved from stdlib into Nimble packages:
- [``basic2d``](https://github.com/nim-lang/basic2d)
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- [``basic3d``](https://github.com/nim-lang/basic3d)
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- [``gentabs``](https://github.com/lcrees/gentabs)
- [``libuv``](https://github.com/lcrees/libuv)
- [``numeric``](https://github.com/lcrees/polynumeric)
- [``poly``](https://github.com/lcrees/polynumeric)
- [``pdcurses``](https://github.com/lcrees/pdcurses)
- [``romans``](https://github.com/lcrees/romans)

View file

@ -798,8 +798,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
origTy: PType) =
var test, u, v: TLoc var test, u, v: TLoc
for i in countup(1, sonsLen(e) - 1): for i in countup(1, sonsLen(e) - 1):
var it = e.sons[i] var it = e.sons[i]
@ -811,12 +810,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
assert(disc.kind == nkSym) assert(disc.kind == nkSym)
initLoc(test, locNone, it, OnStack) initLoc(test, locNone, it, OnStack)
initLocExpr(p, it.sons[1], u) initLocExpr(p, it.sons[1], u)
var o = obj
let d = lookupFieldAgain(p, origTy, disc.sym, o)
initLoc(v, locExpr, disc, OnUnknown) initLoc(v, locExpr, disc, OnUnknown)
v.r = o v.r = obj
v.r.add(".") v.r.add(".")
v.r.add(d.loc.r) v.r.add(disc.sym.loc.r)
genInExprAux(p, it, u, v, test) genInExprAux(p, it, u, v, test)
let id = nodeTableTestOrSet(p.module.dataCache, let id = nodeTableTestOrSet(p.module.dataCache,
newStrNode(nkStrLit, field.name.s), p.module.labels) newStrNode(nkStrLit, field.name.s), p.module.labels)
@ -842,7 +839,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: fillObjectFields(p.module, ty)
if field.loc.r == nil: if field.loc.r == nil:
internalError(e.info, "genCheckedRecordField") # generate the checks: internalError(e.info, "genCheckedRecordField") # generate the checks:
genFieldCheck(p, e, r, field, ty) genFieldCheck(p, e, r, field)
add(r, rfmt(nil, ".$1", field.loc.r)) add(r, rfmt(nil, ".$1", field.loc.r))
putIntoDest(p, d, e.sons[0], r, a.storage) putIntoDest(p, d, e.sons[0], r, a.storage)
else: else:
@ -1226,7 +1223,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: fillObjectFields(p.module, ty)
if field.loc.r == nil: internalError(e.info, "genObjConstr") if field.loc.r == nil: internalError(e.info, "genObjConstr")
if it.len == 3 and optFieldCheck in p.options: if it.len == 3 and optFieldCheck in p.options:
genFieldCheck(p, it.sons[2], r, field, ty) genFieldCheck(p, it.sons[2], r, field)
add(tmp2.r, ".") add(tmp2.r, ".")
add(tmp2.r, field.loc.r) add(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
@ -1644,6 +1641,12 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
elif etyp.kind == tyProc and etyp.callConv == ccClosure: elif etyp.kind == tyProc and etyp.callConv == ccClosure:
putIntoDest(p, d, e, "(($1) ($2))" % putIntoDest(p, d, e, "(($1) ($2))" %
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
else:
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: else:
putIntoDest(p, d, e, "(($1) ($2))" % putIntoDest(p, d, e, "(($1) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
@ -2275,7 +2278,13 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
result = "{{$1}}" % [genTypeInfo(p.module, t, info)] result = "{{$1}}" % [genTypeInfo(p.module, t, info)]
else: else:
result = rope"{}" result = rope"{}"
of tyArray, tyTuple: result = rope"{}" of tyTuple:
result = rope"{"
for i in 0 ..< typ.len:
if i > 0: result.add ", "
result.add getDefaultValue(p, typ.sons[i], info)
result.add "}"
of tyArray: result = rope"{}"
of tySet: of tySet:
if mapType(t) == ctArray: result = rope"{}" if mapType(t) == ctArray: result = rope"{}"
else: result = rope"0" else: result = rope"0"

View file

@ -896,14 +896,15 @@ proc addIntTypes(result: var Rope) {.inline.} =
platform.CPU[targetCPU].intSize.rope]) platform.CPU[targetCPU].intSize.rope])
proc getCopyright(cfile: Cfile): Rope = proc getCopyright(cfile: Cfile): Rope =
const copyrightYear = "2017"
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N") % "/* The generated code is subject to the original license. */$N") %
[rope(VersionAsString)] [rope(VersionAsString)]
else: else:
result = ("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N" & "/* The generated code is subject to the original license. */$N" &
"/* Compiled for: $2, $3, $4 */$N" & "/* Compiled for: $2, $3, $4 */$N" &
"/* Command for C compiler:$n $5 */$N") % "/* Command for C compiler:$n $5 */$N") %
@ -1292,7 +1293,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
proc writeHeader(m: BModule) = proc writeHeader(m: BModule) =
var result = ("/* Generated by Nim Compiler v$1 */$N" & var result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & "/* (c) 2017 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N") % "/* The generated code is subject to the original license. */$N") %
[rope(VersionAsString)] [rope(VersionAsString)]

View file

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

View file

@ -608,6 +608,52 @@ proc generateJson*(d: PDoc, n: PNode) =
generateJson(d, lastSon(n.sons[0])) generateJson(d, lastSon(n.sons[0]))
else: discard else: discard
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
var
name = getName(d, nameNode)
result = name & "\n"
proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
case n.kind
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
r.add stripped
of nkProcDef:
when useEffectSystem: documentRaises(n)
r.add genTagsItem(d, n, n.sons[namePos], skProc)
of nkFuncDef:
when useEffectSystem: documentRaises(n)
r.add genTagsItem(d, n, n.sons[namePos], skFunc)
of nkMethodDef:
when useEffectSystem: documentRaises(n)
r.add genTagsItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef:
when useEffectSystem: documentRaises(n)
r.add genTagsItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef:
r.add genTagsItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef:
r.add genTagsItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef:
when useEffectSystem: documentRaises(n)
r.add genTagsItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
# order is always 'type var let const':
r.add genTagsItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList:
for i in countup(0, sonsLen(n) - 1):
generateTags(d, n.sons[i], r)
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]):
generateTags(d, lastSon(n.sons[0]), r)
else: discard
proc genSection(d: PDoc, kind: TSymKind) = proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [ const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
@ -745,6 +791,21 @@ proc commandJson*() =
#echo getOutFile(gProjectFull, JsonExt) #echo getOutFile(gProjectFull, JsonExt)
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandTags*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
var
content: Rope
generateTags(d, ast, content)
if optStdout in gGlobalOptions:
writeRope(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false)
proc commandBuildIndex*() = proc commandBuildIndex*() =
var content = mergeIndexes(gProjectFull).rope var content = mergeIndexes(gProjectFull).rope

View file

@ -52,7 +52,7 @@ proc isLet(n: PNode): bool =
proc isVar(n: PNode): bool = proc isVar(n: PNode): bool =
n.kind == nkSym and n.sym.kind in {skResult, skVar} and n.kind == nkSym and n.sym.kind in {skResult, skVar} and
{sfGlobal, sfAddrTaken} * n.sym.flags == {} {sfAddrTaken} * n.sym.flags == {}
proc isLetLocation(m: PNode, isApprox: bool): bool = proc isLetLocation(m: PNode, isApprox: bool): bool =
# consider: 'n[].kind' --> we really need to support 1 deref op even if this # consider: 'n[].kind' --> we really need to support 1 deref op even if this
@ -768,9 +768,11 @@ macro `=~`(x: PNode, pat: untyped): bool =
var conds = newTree(nnkBracket) var conds = newTree(nnkBracket)
m(x, pat, conds) m(x, pat, conds)
when declared(macros.toNimIdent):
result = nestList(toNimIdent"and", conds)
else:
result = nestList(!"and", conds) result = nestList(!"and", conds)
proc isMinusOne(n: PNode): bool = proc isMinusOne(n: PNode): bool =
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1 n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1420,9 +1420,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: result = semGeneric(c, n, s, prev) else: result = semGeneric(c, n, s, prev)
of nkDotExpr: of nkDotExpr:
let typeExpr = semExpr(c, n) let typeExpr = semExpr(c, n)
if typeExpr.typ.kind == tyFromExpr: if typeExpr.typ.isNil:
return typeExpr.typ localError(n.info, "object constructor needs an object type;" &
if typeExpr.typ.kind != tyTypeDesc: " for named arguments use '=' instead of ':'")
result = errorType(c)
elif typeExpr.typ.kind == tyFromExpr:
result = typeExpr.typ
elif typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
result = errorType(c) result = errorType(c)
else: else:

View file

@ -1385,7 +1385,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
# XXX: This is very hacky. It should be moved back into liftTypeParam # XXX: This is very hacky. It should be moved back into liftTypeParam
if x.kind in {tyGenericInst, tyArray} and if x.kind in {tyGenericInst, tyArray} and
c.calleeSym != nil and c.calleeSym != nil and
c.calleeSym.kind in {skProc, skFunc}: c.calleeSym.kind in {skProc, skFunc} and c.call != nil:
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
return typeRel(c, inst, a) return typeRel(c, inst, a)
@ -1613,7 +1613,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
if not exprStructuralEquivalent(f.n, aOrig.n): if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone result = isNone
if result != isNone: put(c, f, aOrig) if result != isNone: put(c, f, aOrig)
elif aOrig.n != nil: elif aOrig.n != nil and aOrig.n.typ != nil:
result = typeRel(c, f.lastSon, aOrig.n.typ) result = typeRel(c, f.lastSon, aOrig.n.typ)
if result != isNone: if result != isNone:
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])

View file

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

View file

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

View file

@ -1810,7 +1810,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
unused(n, dest) unused(n, dest)
genVarSection(c, n) genVarSection(c, n)
of declarativeDefs: of declarativeDefs, nkMacroDef:
unused(n, dest) unused(n, dest)
of nkLambdaKinds: of nkLambdaKinds:
#let s = n.sons[namePos].sym #let s = n.sons[namePos].sym

View file

@ -7,7 +7,8 @@ Advanced commands:
//rst2html convert a reStructuredText file to HTML //rst2html convert a reStructuredText file to HTML
//rst2tex convert a reStructuredText file to TeX //rst2tex convert a reStructuredText file to TeX
//jsondoc extract the documentation to a json file //jsondoc extract the documentation to a json file
//jsondoc2 extract documentation to a json file (uses doc2) //jsondoc2 extract the documentation to a json file (uses doc2)
//ctags create a tags file
//buildIndex build an index for the whole documentation //buildIndex build an index for the whole documentation
//run run the project (with Tiny C backend; buggy!) //run run the project (with Tiny C backend; buggy!)
//genDepend generate a DOT file containing the //genDepend generate a DOT file containing the

View file

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

View file

@ -64,7 +64,6 @@ Core
* `cpuinfo <cpuinfo.html>`_ * `cpuinfo <cpuinfo.html>`_
This module implements procs to determine the number of CPUs / cores. This module implements procs to determine the number of CPUs / cores.
Collections and algorithms Collections and algorithms
-------------------------- --------------------------
@ -182,6 +181,12 @@ Generic Operating System Services
This module implements asynchronous file reading and writing using This module implements asynchronous file reading and writing using
``asyncdispatch``. ``asyncdispatch``.
* `distros <distros.html>`_
This module implements the basics for OS distribution ("distro") detection and the OS's native package manager.
Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum
that is useful for writing platform specific code.
Math libraries Math libraries
-------------- --------------

View file

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

View file

@ -102,6 +102,14 @@ collector to not consider objects of this type as part of a cycle:
left, right: Node left, right: Node
data: string data: string
Or if we directly use a ref object:
.. code-block:: nim
type
Node = ref object {.acyclic, final.}
left, right: Node
data: string
In the example a tree structure is declared with the ``Node`` type. Note that In the example a tree structure is declared with the ``Node`` type. Note that
the type definition is recursive and the GC has to assume that objects of the type definition is recursive and the GC has to assume that objects of
this type may form a cyclic graph. The ``acyclic`` pragma passes the this type may form a cyclic graph. The ``acyclic`` pragma passes the

View file

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

View file

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

View file

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

View file

@ -85,7 +85,7 @@
<int key="IBUITag">2</int> <int key="IBUITag">2</int>
<bool key="IBUIUserInteractionEnabled">NO</bool> <bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Nimrod Crossplatform Calculator</string> <string key="IBUIText">Nim Crossplatform Calculator</string>
<object class="NSColor" key="IBUITextColor" id="128895179"> <object class="NSColor" key="IBUITextColor" id="128895179">
<int key="NSColorSpace">1</int> <int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes> <bytes key="NSRGB">MCAwIDAAA</bytes>

View file

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

View file

@ -1,4 +1,4 @@
# Nimrod configuration file. # Nim configuration file.
# The file is used only to add the path of the backend to the compiler options. # The file is used only to add the path of the backend to the compiler options.
path="../nim_backend" path="../nim_backend"

View file

@ -2,7 +2,7 @@ The cross platform calculator illustrates how to use Nim to create a backend
called by different native user interfaces. called by different native user interfaces.
Since the purpose of the example is to show how the cross platform code Since the purpose of the example is to show how the cross platform code
interacts with Nimrod the actual backend code is just a simple addition proc. interacts with Nim the actual backend code is just a simple addition proc.
By keeping your program logic in Nim you can easily reuse it in different By keeping your program logic in Nim you can easily reuse it in different
platforms. platforms.

View file

@ -4,9 +4,8 @@
import db_sqlite, parseutils, strutils, times import db_sqlite, parseutils, strutils, times
type type
TTodo* = object Todo* = object
## A todo object holding the information serialized to the database. ## A todo object holding the information serialized to the database.
id: int64 ## Unique identifier of the object in the id: int64 ## Unique identifier of the object in the
## database, use the getId() accessor to read it. ## database, use the getId() accessor to read it.
@ -17,7 +16,7 @@ type
## outside of this module, use the ## outside of this module, use the
## getModificationDate accessor. ## getModificationDate accessor.
TPagedParams* = object PagedParams* = object
## Contains parameters for a query, initialize default values with ## Contains parameters for a query, initialize default values with
## initDefaults(). ## initDefaults().
pageSize*: int64 ## Lines per returned query page, -1 for pageSize*: int64 ## Lines per returned query page, -1 for
@ -27,11 +26,10 @@ type
showUnchecked*: bool ## Get unchecked objects. showUnchecked*: bool ## Get unchecked objects.
showChecked*: bool ## Get checked objects. showChecked*: bool ## Get checked objects.
# - General procs # - General procs
#
proc initDefaults*(params: var TPagedParams) = proc initDefaults*(params: var PagedParams) =
## Sets sane defaults for a TPagedParams object. ## Sets sane defaults for a PagedParams object.
## ##
## Note that you should always provide a non zero pageSize, either a specific ## Note that you should always provide a non zero pageSize, either a specific
## positive value or negative for unbounded query results. ## positive value or negative for unbounded query results.
@ -41,7 +39,6 @@ proc initDefaults*(params: var TPagedParams) =
params.showUnchecked = true params.showUnchecked = true
params.showChecked = false params.showChecked = false
proc openDatabase*(path: string): DbConn = proc openDatabase*(path: string): DbConn =
## Creates or opens the sqlite3 database. ## Creates or opens the sqlite3 database.
## ##
@ -56,16 +53,14 @@ proc openDatabase*(path: string): DbConn =
desc TEXT NOT NULL, desc TEXT NOT NULL,
modification_date INTEGER NOT NULL, modification_date INTEGER NOT NULL,
CONSTRAINT Todos UNIQUE (id))""" CONSTRAINT Todos UNIQUE (id))"""
db_sqlite.exec(conn, query) db_sqlite.exec(conn, query)
result = conn result = conn
# - Procs related to Todo objects
# - Procs related to TTodo objects
#
proc initFromDB(id: int64; text: string; priority: int, isDone: bool; proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
modificationDate: Time): TTodo = modificationDate: Time): Todo =
## Returns an initialized TTodo object created from database parameters. ## Returns an initialized Todo object created from database parameters.
## ##
## The proc assumes all values are right. Note this proc is NOT exported. ## The proc assumes all values are right. Note this proc is NOT exported.
assert(id >= 0, "Identity identifiers should not be negative") assert(id >= 0, "Identity identifiers should not be negative")
@ -75,29 +70,25 @@ proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
result.isDone = isDone result.isDone = isDone
result.modificationDate = modificationDate result.modificationDate = modificationDate
proc getId*(todo: Todo): int64 =
proc getId*(todo: TTodo): int64 =
## Accessor returning the value of the private id property. ## Accessor returning the value of the private id property.
return todo.id return todo.id
proc getModificationDate*(todo: Todo): Time =
proc getModificationDate*(todo: TTodo): Time = ## Returns the last modification date of a Todo entry.
## Returns the last modification date of a TTodo entry.
return todo.modificationDate return todo.modificationDate
proc update*(todo: var Todo; conn: DbConn): bool =
proc update*(todo: var TTodo; conn: DbConn): bool =
## Checks the database for the object and refreshes its variables. ## Checks the database for the object and refreshes its variables.
## ##
## Use this method if you (or another entity) have modified the database and ## Use this method if you (or another entity) have modified the database and
## want to update the object you have with whatever the database has stored. ## want to update the object you have with whatever the database has stored.
## Returns true if the update succeeded, or false if the object was not found ## Returns true if the update succeeded, or false if the object was not found
## in the database any more, in which case you should probably get rid of the ## in the database any more, in which case you should probably get rid of the
## TTodo object. ## Todo object.
assert(todo.id >= 0, "The identifier of the todo entry can't be negative") assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
let query = sql"""SELECT desc, priority, is_done, modification_date let query = sql"""SELECT desc, priority, is_done, modification_date
FROM Todos WHERE id = ?""" FROM Todos WHERE id = ?"""
try: try:
let rows = conn.getAllRows(query, $todo.id) let rows = conn.getAllRows(query, $todo.id)
if len(rows) < 1: if len(rows) < 1:
@ -111,8 +102,7 @@ proc update*(todo: var TTodo; conn: DbConn): bool =
except: except:
echo("Something went wrong selecting for id " & $todo.id) echo("Something went wrong selecting for id " & $todo.id)
proc save*(todo: var Todo; conn: DbConn): bool =
proc save*(todo: var TTodo; conn: DbConn): bool =
## Saves the current state of text, priority and isDone to the database. ## Saves the current state of text, priority and isDone to the database.
## ##
## Returns true if the database object was updated (in which case the ## Returns true if the database object was updated (in which case the
@ -127,15 +117,13 @@ proc save*(todo: var TTodo; conn: DbConn): bool =
WHERE id = ?""" WHERE id = ?"""
rowsUpdated = conn.execAffectedRows(query, $todo.text, rowsUpdated = conn.execAffectedRows(query, $todo.text,
$todo.priority, $todo.isDone, $int(currentDate), $todo.id) $todo.priority, $todo.isDone, $int(currentDate), $todo.id)
if 1 == rowsUpdated: if 1 == rowsUpdated:
todo.modificationDate = currentDate todo.modificationDate = currentDate
result = true result = true
# - Procs dealing directly with the database # - Procs dealing directly with the database
#
proc addTodo*(conn: DbConn; priority: int; text: string): TTodo = proc addTodo*(conn: DbConn; priority: int; text: string): Todo =
## Inserts a new todo into the database. ## Inserts a new todo into the database.
## ##
## Returns the generated todo object. If there is an error EDb will be raised. ## Returns the generated todo object. If there is an error EDb will be raised.
@ -145,10 +133,8 @@ proc addTodo*(conn: DbConn; priority: int; text: string): TTodo =
(priority, is_done, desc, modification_date) (priority, is_done, desc, modification_date)
VALUES (?, 'false', ?, ?)""" VALUES (?, 'false', ?, ?)"""
todoId = conn.insertId(query, priority, text, $int(currentDate)) todoId = conn.insertId(query, priority, text, $int(currentDate))
result = initFromDB(todoId, text, priority, false, currentDate) result = initFromDB(todoId, text, priority, false, currentDate)
proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} = proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
## Deletes the specified todo identifier. ## Deletes the specified todo identifier.
## ##
@ -156,7 +142,6 @@ proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
let query = sql"""DELETE FROM Todos WHERE id = ?""" let query = sql"""DELETE FROM Todos WHERE id = ?"""
result = conn.execAffectedRows(query, $todoId) result = conn.execAffectedRows(query, $todoId)
proc getNumEntries*(conn: DbConn): int = proc getNumEntries*(conn: DbConn): int =
## Returns the number of entries in the Todos table. ## Returns the number of entries in the Todos table.
## ##
@ -170,38 +155,30 @@ proc getNumEntries*(conn: DbConn): int =
echo("Something went wrong retrieving number of Todos entries") echo("Something went wrong retrieving number of Todos entries")
result = -1 result = -1
proc getPagedTodos*(conn: DbConn; params: PagedParams; page = 0'i64): seq[Todo] =
proc getPagedTodos*(conn: DbConn; params: TPagedParams;
page = 0'i64): seq[TTodo] =
## Returns the todo entries for a specific page. ## Returns the todo entries for a specific page.
## ##
## Pages are calculated based on the params.pageSize parameter, which can be ## Pages are calculated based on the params.pageSize parameter, which can be
## set to a negative value to specify no limit at all. The query will be ## set to a negative value to specify no limit at all. The query will be
## affected by the TPagedParams, which should have sane values (call ## affected by the PagedParams, which should have sane values (call
## initDefaults). ## initDefaults).
assert(page >= 0, "You should request a page zero or bigger than zero") assert(page >= 0, "You should request a page zero or bigger than zero")
result = @[] result = @[]
# Well, if you don't want to see anything, there's no point in asking the db. # Well, if you don't want to see anything, there's no point in asking the db.
if not params.showUnchecked and not params.showChecked: return if not params.showUnchecked and not params.showChecked: return
let let
order_by = [ order_by = [
if params.priorityAscending: "ASC" else: "DESC", if params.priorityAscending: "ASC" else: "DESC",
if params.dateAscending: "ASC" else: "DESC"] if params.dateAscending: "ASC" else: "DESC"]
query = sql("""SELECT id, desc, priority, is_done, modification_date query = sql("""SELECT id, desc, priority, is_done, modification_date
FROM Todos FROM Todos
WHERE is_done = ? OR is_done = ? WHERE is_done = ? OR is_done = ?
ORDER BY priority $1, modification_date $2, id DESC ORDER BY priority $1, modification_date $2, id DESC
LIMIT ? * ?,?""" % order_by) LIMIT ? * ?,?""" % order_by)
args = @[$params.showChecked, $(not params.showUnchecked), args = @[$params.showChecked, $(not params.showUnchecked),
$params.pageSize, $page, $params.pageSize] $params.pageSize, $page, $params.pageSize]
#echo("Query " & string(query)) #echo("Query " & string(query))
#echo("args: " & args.join(", ")) #echo("args: " & args.join(", "))
var newId: BiggestInt var newId: BiggestInt
for row in conn.fastRows(query, args): for row in conn.fastRows(query, args):
let numChars = row[0].parseBiggestInt(newId) let numChars = row[0].parseBiggestInt(newId)
@ -209,10 +186,9 @@ proc getPagedTodos*(conn: DbConn; params: TPagedParams;
result.add(initFromDB(int64(newId), row[1], row[2].parseInt, result.add(initFromDB(int64(newId), row[1], row[2].parseInt,
row[3].parseBool, Time(row[4].parseInt))) row[3].parseBool, Time(row[4].parseInt)))
proc getTodo*(conn: DbConn; todoId: int64): ref Todo =
proc getTodo*(conn: DbConn; todoId: int64): ref TTodo = ## Returns a reference to a Todo or nil if the todo could not be found.
## Returns a reference to a TTodo or nil if the todo could not be found. var tempTodo: Todo
var tempTodo: TTodo
tempTodo.id = todoId tempTodo.id = todoId
if tempTodo.update(conn): if tempTodo.update(conn):
new(result) new(result)

View file

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

View file

@ -1,4 +1,4 @@
# Nimrod configuration file. # Nim configuration file.
# The file is used only to add the path of the backend to the compiler options. # The file is used only to add the path of the backend to the compiler options.
path="../nim_backend" path="../nim_backend"

View file

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

View file

@ -14,6 +14,6 @@ generation switch can be used to fill the database with some basic todo entries
you can play with. you can play with.
Compilation is fairly easy despite having the source split in different Compilation is fairly easy despite having the source split in different
directories. Thanks to the Nim.cfg file, which adds the ../Nim_backend directories. Thanks to the nim.cfg file, which adds the ../Nim_backend
directory as a search path, you can compile and run the example just fine from directory as a search path, you can compile and run the example just fine from
the command line with 'nim c -r nimtodo.nim'. the command line with 'nim c -r nimtodo.nim'.

View file

@ -1,4 +1,4 @@
The cross platform todo illustrates how to use Nim to create a backend This cross platform todo illustrates how to use Nim to create a backend
called by different native user interfaces. called by different native user interfaces.
This example builds on the knowledge learned from the cross_calculator example. This example builds on the knowledge learned from the cross_calculator example.

View file

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

View file

@ -1,4 +1,4 @@
In this directory you will find several examples for how to use the Nimrod In this directory you will find several examples for how to use the Nim
library. library.
Copyright (c) 2004-2012 Andreas Rumpf. Copyright (c) 2004-2012 Andreas Rumpf.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -113,7 +113,9 @@ type
type type
NimIdent* = object of RootObj NimIdent* = object of RootObj
## represents a Nim identifier in the AST ## represents a Nim identifier in the AST. **Note**: This is only
## rarely useful, for identifier construction from a string
## use ``ident"abc"``.
NimSymObj = object # hidden NimSymObj = object # hidden
NimSym* = ref NimSymObj NimSym* = ref NimSymObj
@ -129,7 +131,11 @@ const
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit} nnkCallStrLit}
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
## constructs an identifier from the string `s` ## constructs an identifier from the string `s`
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
@ -237,7 +243,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.} proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
proc newNimNode*(kind: NimNodeKind, proc newNimNode*(kind: NimNodeKind,
lineInfoFrom: NimNode=nil): NimNode lineInfoFrom: NimNode = nil): NimNode
{.magic: "NNewNimNode", noSideEffect.} {.magic: "NNewNimNode", noSideEffect.}
## Creates a new AST node of the specified kind. ## Creates a new AST node of the specified kind.
## ##
@ -290,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
proc newIdentNode*(i: string): NimNode {.compileTime.} = proc newIdentNode*(i: string): NimNode {.compileTime.} =
## creates an identifier node from `i` ## creates an identifier node from `i`
result = newNimNode(nnkIdent) result = newNimNode(nnkIdent)
result.ident = !i result.ident = toNimIdent i
type type
@ -400,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## macro check(ex: expr): stmt = ## macro check(ex: untyped): typed =
## # this is a simplified version of the check macro from the ## # this is a simplified version of the check macro from the
## # unittest module. ## # unittest module.
## ##
@ -477,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
result = newNimNode(nnkCharLit) result = newNimNode(nnkCharLit)
result.intVal = ord(c) result.intVal = ord(c)
proc newLit*(i: int): NimNode {.compileTime.} = proc newLit*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node. ## produces a new integer literal node.
result = newNimNode(nnkIntLit) result = newNimNode(nnkIntLit)
@ -581,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
proc newLit*(arg: tuple): NimNode {.compileTime.} = proc newLit*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree result = nnkPar.newTree
for a,b in arg.fieldPairs: for a,b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
proc newLit*(s: string): NimNode {.compileTime.} = proc newLit*(s: string): NimNode {.compileTime.} =
## produces a new string literal node. ## produces a new string literal node.
@ -615,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
of nnkIdent: res.add(" !\"" & $n.ident & '"') of nnkIdent: res.add(" ident\"" & $n.ident & '"')
of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkSym: res.add(" \"" & $n.symbol & '"')
of nnkNone: assert false of nnkNone: assert false
else: else:
@ -640,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"') of nnkIdent: add(result, "ident\"" & $n.ident & '"')
of nnkSym: add(result, $n.symbol) of nnkSym: add(result, $n.symbol)
of nnkNone: assert false of nnkNone: assert false
else: else:
@ -664,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## .. code-block:: nim ## .. code-block:: nim
## nnkStmtList.newTree( ## nnkStmtList.newTree(
## nnkCommand.newTree( ## nnkCommand.newTree(
## newIdentNode(!"echo"), ## newIdentNode("echo"),
## newLit("Hello world") ## newLit("Hello world")
## ) ## )
## ) ## )
@ -718,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
of nnkIdent: res.add("!" & ($n.ident).escape()) of nnkIdent: res.add(($n.ident).escape())
of nnkSym: res.add(($n.symbol).escape()) of nnkSym: res.add(($n.symbol).escape())
of nnkNone: assert false of nnkNone: assert false
else: else:
@ -898,6 +903,48 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
for i in branches: for i in branches:
result.add(newNimNode(nnkElifBranch).add(i.cond, i.body)) result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
proc newEnum*(name: NimNode, fields: openArray[NimNode],
public, pure: bool): NimNode {.compileTime.} =
## Creates a new enum. `name` must be an ident. Fields are allowed to be
## either idents or EnumFieldDef
##
## .. code-block:: nim
##
## newEnum(
## name = ident("Colors"),
## fields = [ident("Blue"), ident("Red")],
## public = true, pure = false)
##
## # type Colors* = Blue Red
##
expectKind name, nnkIdent
doAssert len(fields) > 0, "Enum must contain at least one field"
for field in fields:
expectKind field, {nnkIdent, nnkEnumFieldDef}
let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields)
var typeDefArgs = [name, newEmptyNode(), enumBody]
if public:
let postNode = newNimNode(nnkPostfix).add(
newIdentNode("*"), typeDefArgs[0])
typeDefArgs[0] = postNode
if pure:
let pragmaNode = newNimNode(nnkPragmaExpr).add(
typeDefArgs[0],
add(newNimNode(nnkPragma), newIdentNode("pure")))
typeDefArgs[0] = pragmaNode
let
typeDef = add(newNimNode(nnkTypeDef), typeDefArgs)
typeSect = add(newNimNode(nnkTypeSection), typeDef)
return typeSect
proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
## Copy all children from `src` to `dest` ## Copy all children from `src` to `dest`
@ -1015,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
## ##
## .. code-block:: nim ## .. code-block:: nim
## var res = findChild(n, it.kind == nnkPostfix and ## var res = findChild(n, it.kind == nnkPostfix and
## it.basename.ident == !"foo") ## it.basename.ident == toNimIdent"foo")
block: block:
var res: NimNode var res: NimNode
for it in n.children: for it in n.children:
@ -1049,7 +1096,7 @@ proc basename*(a: NimNode): NimNode =
proc `basename=`*(a: NimNode; val: string) {.compileTime.}= proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
case a.kind case a.kind
of nnkIdent: macros.`ident=`(a, !val) of nnkIdent: macros.`ident=`(a, toNimIdent val)
of nnkPostfix, nnkPrefix: a[1] = ident(val) of nnkPostfix, nnkPrefix: a[1] = ident(val)
else: else:
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a) quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
@ -1110,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## other ways like ``node.ident`` are much more error-prone, unfortunately. ## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind case node.kind
of nnkIdent: of nnkIdent:
result = node.ident == !s result = node.ident == toNimIdent s
of nnkSym: of nnkSym:
result = eqIdent($node.symbol, s) result = eqIdent($node.symbol, s)
of nnkOpenSymChoice, nnkClosedSymChoice: of nnkOpenSymChoice, nnkClosedSymChoice:

View file

@ -3,12 +3,12 @@ export nativesockets
{.warning: "rawsockets module is deprecated, use nativesockets instead".} {.warning: "rawsockets module is deprecated, use nativesockets instead".}
template newRawSocket*(domain, sockType, protocol: cint): expr = template newRawSocket*(domain, sockType, protocol: cint): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".} {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol) newNativeSocket(domain, sockType, protocol)
template newRawSocket*(domain: Domain = AF_INET, template newRawSocket*(domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM, sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): expr = protocol: Protocol = IPPROTO_TCP): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".} {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol) newNativeSocket(domain, sockType, protocol)

View file

@ -58,15 +58,18 @@ type
socket: AsyncSocket socket: AsyncSocket
reuseAddr: bool reuseAddr: bool
reusePort: bool reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer, {.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].} THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer = proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance. ## Creates a new ``AsyncHttpServer`` instance.
new result new result
result.reuseAddr = reuseAddr result.reuseAddr = reuseAddr
result.reusePort = reusePort result.reusePort = reusePort
result.maxBody = maxBody
proc addHeaders(msg: var string, headers: HttpHeaders) = proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers: for k, v in headers:
@ -122,24 +125,23 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
raise newException(ValueError, "Invalid request protocol. Got: " & raise newException(ValueError, "Invalid request protocol. Got: " &
protocol) protocol)
result.orig = protocol result.orig = protocol
i.inc protocol.parseInt(result.major, i) i.inc protocol.parseSaturatedNatural(result.major, i)
i.inc # Skip . i.inc # Skip .
i.inc protocol.parseInt(result.minor, i) i.inc protocol.parseSaturatedNatural(result.minor, i)
proc sendStatus(client: AsyncSocket, status: string): Future[void] = proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L") client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string, proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request): callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} = Future[void] {.closure, gcsafe.}) {.async.} =
var request: Request
request.url = initUri()
request.headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
var key, value = ""
while not client.isClosed: # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
req.mget()
# GET /path HTTP/1.1 # GET /path HTTP/1.1
# Header: val # Header: val
# \n # \n
@ -177,22 +179,22 @@ proc processClient(client: AsyncSocket, address: string,
request.reqMethod = parseEnum[HttpMethod]("http" & linePart) request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
except ValueError: except ValueError:
asyncCheck request.respondError(Http400) asyncCheck request.respondError(Http400)
continue return
of 1: of 1:
try: try:
parseUri(linePart, request.url) parseUri(linePart, request.url)
except ValueError: except ValueError:
asyncCheck request.respondError(Http400) asyncCheck request.respondError(Http400)
continue return
of 2: of 2:
try: try:
request.protocol = parseProtocol(linePart) request.protocol = parseProtocol(linePart)
except ValueError: except ValueError:
asyncCheck request.respondError(Http400) asyncCheck request.respondError(Http400)
continue return
else: else:
await request.respondError(Http400) await request.respondError(Http400)
continue return
inc i inc i
# Headers # Headers
@ -228,18 +230,20 @@ proc processClient(client: AsyncSocket, address: string,
# - Check for Content-length header # - Check for Content-length header
if request.headers.hasKey("Content-Length"): if request.headers.hasKey("Content-Length"):
var contentLength = 0 var contentLength = 0
if parseInt(request.headers["Content-Length"], if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.") await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue return
else: else:
if contentLength > server.maxBody:
await request.respondError(Http413)
return
request.body = await client.recv(contentLength) request.body = await client.recv(contentLength)
if request.body.len != contentLength: if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.") await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
continue return
elif request.reqMethod == HttpPost: elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.") await request.respond(Http411, "Content-Length required.")
continue return
# Call the user's callback. # Call the user's callback.
await callback(request) await callback(request)
@ -249,9 +253,9 @@ proc processClient(client: AsyncSocket, address: string,
# Persistent connections # Persistent connections
if (request.protocol == HttpVer11 and if (request.protocol == HttpVer11 and
request.headers.getOrDefault("connection").normalize != "close") or cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
(request.protocol == HttpVer10 and (request.protocol == HttpVer10 and
request.headers.getOrDefault("connection").normalize == "keep-alive"): cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
# In HTTP 1.1 we assume that connection is persistent. Unless connection # In HTTP 1.1 we assume that connection is persistent. Unless connection
# header states otherwise. # header states otherwise.
# In HTTP 1.0 we assume that the connection should not be persistent. # In HTTP 1.0 we assume that the connection should not be persistent.
@ -259,7 +263,22 @@ proc processClient(client: AsyncSocket, address: string,
discard discard
else: else:
request.client.close() request.client.close()
break return
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
var request = newFutureVar[Request]("asynchttpserver.processClient")
request.mget().url = initUri()
request.mget().headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
while not client.isClosed:
try:
await processRequest(server, request, client, address, lineFut, callback)
except:
asyncCheck request.mget().respondError(Http500)
proc serve*(server: AsyncHttpServer, port: Port, proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: Request): Future[void] {.closure,gcsafe.}, callback: proc (request: Request): Future[void] {.closure,gcsafe.},
@ -280,7 +299,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
# TODO: Causes compiler crash. # TODO: Causes compiler crash.
#var (address, client) = await server.socket.acceptAddr() #var (address, client) = await server.socket.acceptAddr()
var fut = await server.socket.acceptAddr() var fut = await server.socket.acceptAddr()
asyncCheck processClient(fut.client, fut.address, callback) asyncCheck processClient(server, fut.client, fut.address, callback)
#echo(f.isNil) #echo(f.isNil)
#echo(f.repr) #echo(f.repr)

View file

@ -472,27 +472,13 @@ proc stripAwait(node: NimNode): NimNode =
proc splitParamType(paramType: NimNode, async: bool): NimNode = proc splitParamType(paramType: NimNode, async: bool): NimNode =
result = paramType result = paramType
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]: if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
let firstType = paramType[1] let firstAsync = "async" in ($paramType[1].ident).normalize
let firstTypeName = $firstType.ident let secondAsync = "async" in ($paramType[2].ident).normalize
let secondType = paramType[2]
let secondTypeName = $secondType.ident
# Make sure that at least one has the name `async`, otherwise we shouldn't if firstAsync:
# touch it. result = paramType[if async: 1 else: 2]
if not ("async" in firstTypeName.normalize or elif secondAsync:
"async" in secondTypeName.normalize): result = paramType[if async: 2 else: 1]
return
if async:
if firstTypeName.normalize.startsWith("async"):
result = paramType[1]
elif secondTypeName.normalize.startsWith("async"):
result = paramType[2]
else:
if not firstTypeName.normalize.startsWith("async"):
result = paramType[1]
elif not secondTypeName.normalize.startsWith("async"):
result = paramType[2]
proc stripReturnType(returnType: NimNode): NimNode = proc stripReturnType(returnType: NimNode): NimNode =
# Strip out the 'Future' from 'Future[T]'. # Strip out the 'Future' from 'Future[T]'.

View file

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

View file

@ -20,6 +20,8 @@
include "system/inclrtl" include "system/inclrtl"
import macros
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
@ -704,6 +706,52 @@ template newSeqWith*(len: int, init: untyped): untyped =
result[i] = init result[i] = init
result result
proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
filter = nnkLiterals): NimNode =
if constructor.kind in filter:
result = newNimNode(nnkCall, lineInfoFrom=constructor)
result.add op
result.add constructor
else:
result = newNimNode(constructor.kind, lineInfoFrom=constructor)
for v in constructor:
if nested or v.kind in filter:
result.add mapLitsImpl(v, op, nested, filter)
else:
result.add v
macro mapLiterals*(constructor, op: untyped;
nested = true): untyped =
## applies ``op`` to each of the **atomic** literals like ``3``
## or ``"abc"`` in the specified ``constructor`` AST. This can
## be used to map every array element to some target type:
##
## Example:
##
## .. code-block::
## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
## doAssert x is array[4, int]
##
## Short notation for:
##
## .. code-block::
## let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
##
## If ``nested`` is true, the literals are replaced everywhere
## in the ``constructor`` AST, otherwise only the first level
## is considered:
##
## .. code-block::
## mapLiterals((1, ("abc"), 2), float, nested=false)
##
## Produces::
##
## (float(1), ("abc"), float(2))
##
## There are no constraints for the ``constructor`` AST, it
## works for nested tuples of arrays of sets etc.
result = mapLitsImpl(constructor, op, nested.boolVal)
when isMainModule: when isMainModule:
import strutils import strutils
block: # concat test block: # concat test
@ -992,5 +1040,11 @@ when isMainModule:
seq2D[0][1] = true seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # mapLiterals tests
let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
doAssert x is array[4, int]
doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
when not defined(testing): when not defined(testing):
echo "Finished doc tests" echo "Finished doc tests"

View file

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

View file

@ -13,6 +13,15 @@ import strtabs, times
proc parseCookies*(s: string): StringTableRef = proc parseCookies*(s: string): StringTableRef =
## parses cookies into a string table. ## parses cookies into a string table.
##
## The proc is meant to parse the Cookie header set by a client, not the
## "Set-Cookie" header set by servers.
##
## Example:
##
## .. code-block::Nim
## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
result = newStringTable(modeCaseInsensitive) result = newStringTable(modeCaseInsensitive)
var i = 0 var i = 0
while true: while true:

View file

@ -1,211 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The ``gentabs`` module implements an efficient hash table that is a
## key-value mapping. The keys are required to be strings, but the values
## may be any Nim or user defined type. This module supports matching
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
##
## **Warning:** This module is deprecated, new code shouldn't use it!
{.deprecated.}
import
os, hashes, strutils
type
GenTableMode* = enum ## describes the table's key matching mode
modeCaseSensitive, ## case sensitive matching of keys
modeCaseInsensitive, ## case insensitive matching of keys
modeStyleInsensitive ## style sensitive matching of keys
GenKeyValuePair[T] = tuple[key: string, val: T]
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
GenTable*[T] = object of RootObj
counter: int
data: GenKeyValuePairSeq[T]
mode: GenTableMode
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
const
growthFactor = 2
startSize = 64
proc len*[T](tbl: PGenTable[T]): int {.inline.} =
## returns the number of keys in `tbl`.
result = tbl.counter
iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
## iterates over any (key, value) pair in the table `tbl`.
for h in 0..high(tbl.data):
if not isNil(tbl.data[h].key):
yield (tbl.data[h].key, tbl.data[h].val)
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
case tbl.mode
of modeCaseSensitive: result = hashes.hash(key)
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
proc myCmp[T](tbl: PGenTable[T], a, b: string): bool =
case tbl.mode
of modeCaseSensitive: result = cmp(a, b) == 0
of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
## creates a new generic hash table that is empty.
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash
proc rawGet[T](tbl: PGenTable[T], key: string): int =
var h: Hash
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
while not isNil(tbl.data[h].key):
if myCmp(tbl, tbl.data[h].key, key):
return h
h = nextTry(h, high(tbl.data))
result = - 1
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
key: string, val: T) =
var h: Hash
h = myhash(tbl, key) and high(data)
while not isNil(data[h].key):
h = nextTry(h, high(data))
data[h].key = key
data[h].val = val
proc enlarge[T](tbl: PGenTable[T]) =
var n: GenKeyValuePairSeq[T]
newSeq(n, len(tbl.data) * growthFactor)
for i in countup(0, high(tbl.data)):
if not isNil(tbl.data[i].key):
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
swap(tbl.data, n)
proc hasKey*[T](tbl: PGenTable[T], key: string): bool =
## returns true iff `key` is in the table `tbl`.
result = rawGet(tbl, key) >= 0
proc `[]`*[T](tbl: PGenTable[T], key: string): T =
## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`,
## default(T) is returned and no exception is raised. One can check
## with ``hasKey`` whether the key exists.
var index = rawGet(tbl, key)
if index >= 0: result = tbl.data[index].val
proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) =
## puts a (key, value)-pair into `tbl`.
var index = rawGet(tbl, key)
if index >= 0:
tbl.data[index].val = val
else:
if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl)
rawInsert(tbl, tbl.data, key, val)
inc(tbl.counter)
when isMainModule:
#
# Verify tables of integer values (string keys)
#
var x = newGenTable[int](modeCaseInsensitive)
x["one"] = 1
x["two"] = 2
x["three"] = 3
x["four"] = 4
x["five"] = 5
assert(len(x) == 5) # length procedure works
assert(x["one"] == 1) # case-sensitive lookup works
assert(x["ONE"] == 1) # case-insensitive should work for this table
assert(x["one"]+x["two"] == 3) # make sure we're getting back ints
assert(x.hasKey("one")) # hasKey should return 'true' for a key
# of "one"...
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
for k,v in pairs(x): # make sure the 'pairs' iterator works
assert(x[k]==v)
#
# Verify a table of user-defined types
#
type
MyType = tuple[first, second: string] # a pair of strings
{.deprecated: [TMyType: MyType].}
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
# value is MyType tuple
#var junk: MyType = ("OK", "Here")
#echo junk.first, " ", junk.second
y["Hello"] = ("Hello", "World")
y["Goodbye"] = ("Goodbye", "Everyone")
#y["Hello"] = MyType( ("Hello", "World") )
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
assert( not isNil(y["Hello"].first) )
assert( y["Hello"].first == "Hello" )
assert( y["Hello"].second == "World" )
#
# Verify table of tables
#
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
# a hash table of ints
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
z["first"] = newGenTable[int](modeCaseInsensitive)
z["first"]["one"] = 1
z["first"]["two"] = 2
z["first"]["three"] = 3
z["second"] = newGenTable[int](modeCaseInsensitive)
z["second"]["red"] = 10
z["second"]["blue"] = 20
assert(len(z) == 2) # length of outer table
assert(len(z["first"]) == 3) # length of "first" table
assert(len(z["second"]) == 2) # length of "second" table
assert( z["first"]["one"] == 1) # retrieve from first inner table
assert( z["second"]["red"] == 10) # retrieve from second inner table
when false:
# disabled: depends on hash order:
var output = ""
for k, v in pairs(z):
output.add( "$# ($#) ->\L" % [k,$len(v)] )
for k2,v2 in pairs(v):
output.add( " $# <-> $#\L" % [k2,$v2] )
let expected = unindent """
first (3) ->
two <-> 2
three <-> 3
one <-> 1
second (2) ->
red <-> 10
blue <-> 20
"""
assert output == expected

View file

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

View file

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

View file

@ -1,87 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Robert Persson
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
type OneVarFunction* = proc (x: float): float
{.deprecated: [TOneVarFunction: OneVarFunction].}
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
tuple[rootx, rooty: float, success: bool]=
## Searches `function` for a root between `xmin` and `xmax`
## using brents method. If the function value at `xmin`and `xmax` has the
## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
## and succes is set to false.
## Otherwise there exists at least one root and success is set to true.
## This root is searched for at most `maxiter` iterations.
## If `tol` tolerance is reached within `maxiter` iterations
## the root refinement stops and success=true.
# see http://en.wikipedia.org/wiki/Brent%27s_method
var
a=xmin
b=xmax
c=a
d=1.0e308
fa=function(a)
fb=function(b)
fc=fa
s=0.0
fs=0.0
mflag:bool
i=0
tmp2:float
if fa*fb>=0:
if abs(fa)<abs(fb):
return (a,fa,false)
else:
return (b,fb,false)
if abs(fa)<abs(fb):
swap(fa,fb)
swap(a,b)
while fb!=0.0 and abs(a-b)>tol:
if fa!=fc and fb!=fc: # inverse quadratic interpolation
s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
else: #secant rule
s = b - fb * (b - a) / (fb - fa)
tmp2 = (3.0 * a + b) / 4.0
if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
(mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
s=(a+b)/2.0
mflag=true
else:
if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
s=(a+b)/2.0
mflag=true
else:
mflag=false
fs = function(s)
d = c
c = b
fc = fb
if fa * fs<0.0:
b=s
fb=fs
else:
a=s
fa=fs
if abs(fa)<abs(fb):
swap(a,b)
swap(fa,fb)
inc i
if i>maxiter:
break
return (b,fb,true)

View file

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

View file

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

View file

@ -252,6 +252,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
elif result != 0: elif result != 0:
number = int(res) number = int(res)
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
## parses a natural number into ``b``. This cannot raise an overflow
## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
## The number of processed character is returned.
## This is usually what you really want to use instead of `parseInt`:idx:.
## Example:
##
## .. code-block:: nim
## var res = 0
## discard parseSaturatedNatural("848", res)
## doAssert res == 848
var i = start
if s[i] == '+': inc(i)
if s[i] in {'0'..'9'}:
b = 0
while s[i] in {'0'..'9'}:
let c = ord(s[i]) - ord('0')
if b <= (high(int) - c) div 10:
b = b * 10 + c
else:
b = high(int)
inc(i)
while s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
# overflowChecks doesn't work with BiggestUInt # overflowChecks doesn't work with BiggestUInt
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
var var
@ -393,16 +418,43 @@ when isMainModule:
let input = "$test{} $this is ${an{ example}} " let input = "$test{} $this is ${an{ example}} "
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
assert toSeq(interpolatedFragments(input)) == expected doAssert toSeq(interpolatedFragments(input)) == expected
var value = 0 var value = 0
discard parseHex("0x38", value) discard parseHex("0x38", value)
assert value == 56 doAssert value == 56
discard parseHex("0x34", value) discard parseHex("0x34", value)
assert value == 56 * 256 + 52 doAssert value == 56 * 256 + 52
value = -1 value = -1
discard parseHex("0x38", value) discard parseHex("0x38", value)
assert value == -200 doAssert value == -200
value = -1
doAssert(parseSaturatedNatural("848", value) == 3)
doAssert value == 848
value = -1
discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("9223372036854775808", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("9223372036854775807", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("18446744073709551616", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("18446744073709551615", value)
doAssert value == high(int)
value = -1
doAssert(parseSaturatedNatural("1_000_000", value) == 9)
doAssert value == 1_000_000
{.pop.} {.pop.}

View file

@ -1,371 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Robert Persson
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
import math
import strutils
import numeric
type
Poly* = object
cofs:seq[float]
{.deprecated: [TPoly: Poly].}
proc degree*(p:Poly):int=
## Returns the degree of the polynomial,
## that is the number of coefficients-1
return p.cofs.len-1
proc eval*(p:Poly,x:float):float=
## Evaluates a polynomial function value for `x`
## quickly using Horners method
var n=p.degree
result=p.cofs[n]
dec n
while n>=0:
result = result*x+p.cofs[n]
dec n
proc `[]` *(p:Poly;idx:int):float=
## Gets a coefficient of the polynomial.
## p[2] will returns the quadric term, p[3] the cubic etc.
## Out of bounds index will return 0.0.
if idx<0 or idx>p.degree:
return 0.0
return p.cofs[idx]
proc `[]=` *(p:var Poly;idx:int,v:float)=
## Sets an coefficient of the polynomial by index.
## p[2] set the quadric term, p[3] the cubic etc.
## If index is out of range for the coefficients,
## the polynomial grows to the smallest needed degree.
assert(idx>=0)
if idx>p.degree: #polynomial must grow
var oldlen=p.cofs.len
p.cofs.setLen(idx+1)
for q in oldlen.. <high(p.cofs):
p.cofs[q]=0.0 #new-grown coefficients set to zero
p.cofs[idx]=v
iterator items*(p:Poly):float=
## Iterates through the coefficients of the polynomial.
var i=p.degree
while i>=0:
yield p[i]
dec i
proc clean*(p:var Poly;zerotol=0.0)=
## Removes leading zero coefficients of the polynomial.
## An optional tolerance can be given for what's considered zero.
var n=p.degree
var relen=false
while n>0 and abs(p[n])<=zerotol: # >0 => keep at least one coefficient
dec n
relen=true
if relen: p.cofs.setLen(n+1)
proc `$` *(p:Poly):string =
## Gets a somewhat reasonable string representation of the polynomial
## The format should be compatible with most online function plotters,
## for example directly in google search
result=""
var first=true #might skip + sign if first coefficient
for idx in countdown(p.degree,0):
let a=p[idx]
if a==0.0:
continue
if a>= 0.0 and not first:
result.add('+')
first=false
if a!=1.0 or idx==0:
result.add(formatFloat(a,ffDefault,0))
if idx>=2:
result.add("x^" & $idx)
elif idx==1:
result.add("x")
if result=="":
result="0"
proc derivative*(p: Poly): Poly=
## Returns a new polynomial, which is the derivative of `p`
newSeq[float](result.cofs,p.degree)
for idx in 0..high(result.cofs):
result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
proc diff*(p:Poly,x:float):float=
## Evaluates the differentiation of a polynomial with
## respect to `x` quickly using a modifed Horners method
var n=p.degree
result=p[n]*float(n)
dec n
while n>=1:
result = result*x+p[n]*float(n)
dec n
proc integral*(p:Poly):Poly=
## Returns a new polynomial which is the indefinite
## integral of `p`. The constant term is set to 0.0
newSeq(result.cofs,p.cofs.len+1)
result.cofs[0]=0.0 #constant arbitrary term, use 0.0
for i in 1..high(result.cofs):
result.cofs[i]=p.cofs[i-1]/float(i)
proc integrate*(p:Poly;xmin,xmax:float):float=
## Computes the definite integral of `p` between `xmin` and `xmax`
## quickly using a modified version of Horners method
var
n=p.degree
s1=p[n]/float(n+1)
s2=s1
fac:float
dec n
while n>=0:
fac=p[n]/float(n+1)
s1 = s1*xmin+fac
s2 = s2*xmax+fac
dec n
result=s2*xmax-s1*xmin
proc initPoly*(cofs:varargs[float]):Poly=
## Initializes a polynomial with given coefficients.
## The most significant coefficient is first, so to create x^2-2x+3:
## intiPoly(1.0,-2.0,3.0)
if len(cofs)<=0:
result.cofs= @[0.0] #need at least one coefficient
else:
# reverse order of coefficients so indexing matches degree of
# coefficient...
result.cofs= @[]
for idx in countdown(cofs.len-1,0):
result.cofs.add(cofs[idx])
result.clean #remove leading zero terms
proc divMod*(p,d:Poly;q,r:var Poly)=
## Divides `p` with `d`, and stores the quotinent in `q` and
## the remainder in `d`
var
pdeg=p.degree
ddeg=d.degree
power=p.degree-d.degree
ratio:float
r.cofs = p.cofs #initial remainder=numerator
if power<0: #denominator is larger than numerator
q.cofs= @ [0.0] #quotinent is 0.0
return # keep remainder as numerator
q.cofs=newSeq[float](power+1)
for i in countdown(pdeg,ddeg):
ratio=r.cofs[i]/d.cofs[ddeg]
q.cofs[i-ddeg]=ratio
r.cofs[i]=0.0
for j in countup(0,<ddeg):
var idx=i-ddeg+j
r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
r.clean # drop zero coefficients in remainder
proc `+` *(p1:Poly,p2:Poly):Poly=
## Adds two polynomials
var n=max(p1.cofs.len,p2.cofs.len)
newSeq(result.cofs,n)
for idx in countup(0,n-1):
result[idx]=p1[idx]+p2[idx]
result.clean # drop zero coefficients in remainder
proc `*` *(p1:Poly,p2:Poly):Poly=
## Multiplies the polynomial `p1` with `p2`
var
d1=p1.degree
d2=p2.degree
n=d1+d2
idx:int
newSeq(result.cofs,n)
for i1 in countup(0,d1):
for i2 in countup(0,d2):
idx=i1+i2
result[idx]=result[idx]+p1[i1]*p2[i2]
result.clean
proc `*` *(p:Poly,f:float):Poly=
## Multiplies the polynomial `p` with a real number
newSeq(result.cofs,p.cofs.len)
for i in 0..high(p.cofs):
result[i]=p.cofs[i]*f
result.clean
proc `*` *(f:float,p:Poly):Poly=
## Multiplies a real number with a polynomial
return p*f
proc `-`*(p:Poly):Poly=
## Negates a polynomial
result=p
for i in countup(0,<result.cofs.len):
result.cofs[i]= -result.cofs[i]
proc `-` *(p1:Poly,p2:Poly):Poly=
## Subtract `p1` with `p2`
var n=max(p1.cofs.len,p2.cofs.len)
newSeq(result.cofs,n)
for idx in countup(0,n-1):
result[idx]=p1[idx]-p2[idx]
result.clean # drop zero coefficients in remainder
proc `/`*(p:Poly,f:float):Poly=
## Divides polynomial `p` with a real number `f`
newSeq(result.cofs,p.cofs.len)
for i in 0..high(p.cofs):
result[i]=p.cofs[i]/f
result.clean
proc `/` *(p,q:Poly):Poly=
## Divides polynomial `p` with polynomial `q`
var dummy:Poly
p.divMod(q,result,dummy)
proc `mod` *(p,q:Poly):Poly=
## Computes the polynomial modulo operation,
## that is the remainder of `p`/`q`
var dummy:Poly
p.divMod(q,dummy,result)
proc normalize*(p:var Poly)=
## Multiplies the polynomial inplace by a term so that
## the leading term is 1.0.
## This might lead to an unstable polynomial
## if the leading term is zero.
p=p/p[p.degree]
proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
## Solves the quadric equation `ax^2+bx+c`, with a possible
## tolerance `zerotol` to find roots of curves just 'touching'
## the x axis. Returns sequence with 0,1 or 2 solutions.
var p,q,d:float
p=b/(2.0*a)
if p==Inf or p==NegInf: #linear equation..
var linrt= -c/b
if linrt==Inf or linrt==NegInf: #constant only
return @[]
return @[linrt]
q=c/a
d=p*p-q
if d<0.0:
#check for inside zerotol range for neg. roots
var err=a*p*p-b*p+c #evaluate error at parabola center axis
if(err<=zerotol): return @[-p]
return @[]
else:
var sr=sqrt(d)
result= @[-sr-p,sr-p]
proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
## helper function for `roots` function
## quickly computes a range, guaranteed to contain
## all the real roots of the polynomial
# see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
var deg=p.degree
var d=p[deg]
var bound1,bound2:float
for i in countup(0,deg):
var c=abs(p.cofs[i]/d)
bound1=max(bound1,c+1.0)
bound2=bound2+c
bound2=max(1.0,bound2)
result.xmax=min(bound1,bound2)
result.xmin= -result.xmax
proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
## helper function for `roots` function
## try to do a numeric search for a single root in range xp0-xp1,
## adding it to `res` (allocating `res` if nil)
var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
if br.success:
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
res.add(br.rootx)
else:
#this might be a 'touching' case, check function value against
#zero tolerance
if abs(br.rooty)<=zerotol:
if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
res.add(br.rootx)
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
## Computes the real roots of the polynomial `p`
## `tol` is the tolerance used to break searching for each root when reached.
## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
## and is used to find roots for curves that only 'touch' the x-axis.
## `mergetol` is the tolerance, of which two x-values are considered being the same root.
## `maxiter` can be used to limit the number of iterations for each root.
## Returns a (possibly empty) sorted sequence with the solutions.
var deg=p.degree
if deg<=0: #constant only => no roots
return @[]
elif p.degree==1: #linear
var linrt= -p.cofs[0]/p.cofs[1]
if linrt==Inf or linrt==NegInf:
return @[] #constant only => no roots
return @[linrt]
elif p.degree==2:
return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
else:
# degree >=3 , find min/max points of polynomial with recursive
# derivative and do a numerical search for root between each min/max
var range=p.getRangeForRoots()
var minmax=p.derivative.roots(tol,zerotol,mergetol)
result= @[]
if minmax!=nil: #ie. we have minimas/maximas in this function
for x in minmax.items:
addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
range.xmin=x
addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)

View file

@ -1,59 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2011 Philippe Lhoste
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Module for converting an integer to a Roman numeral.
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
##
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
const
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
proc romanToDecimal*(romanVal: string): int =
## Converts a Roman numeral to its int representation.
result = 0
var prevVal = 0
for i in countdown(romanVal.len - 1, 0):
var val = 0
case romanVal[i]
of 'I', 'i': val = 1
of 'V', 'v': val = 5
of 'X', 'x': val = 10
of 'L', 'l': val = 50
of 'C', 'c': val = 100
of 'D', 'd': val = 500
of 'M', 'm': val = 1000
else:
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
if val >= prevVal:
inc(result, val)
else:
dec(result, val)
prevVal = val
proc decimalToRoman*(number: range[1..3_999]): string =
## Converts a number to a Roman numeral.
const romanComposites = [
("M", 1000), ("CM", 900),
("D", 500), ("CD", 400), ("C", 100),
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
("V", 5), ("IV", 4), ("I", 1)]
result = ""
var decVal: int = number
for key, val in items(romanComposites):
while decVal >= val:
dec(decVal, val)
result.add(key)
when isMainModule:
for i in 1 .. 3_999:
assert i == i.decimalToRoman.romanToDecimal

View file

@ -1927,7 +1927,7 @@ type
{.deprecated: [TFloatFormat: FloatFormatMode].} {.deprecated: [TFloatFormat: FloatFormatMode].}
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16; precision: range[-1..32] = 16;
decimalSep = '.'): string {. decimalSep = '.'): string {.
noSideEffect, rtl, extern: "nsu$1".} = noSideEffect, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string. ## Converts a floating point value `f` to a string.
@ -1939,7 +1939,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
## `precision`'s default value is the maximum number of meaningful digits ## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``biggestFloat`` type. ## after the decimal point for Nim's ``biggestFloat`` type.
## ##
## If ``precision == 0``, it tries to format it nicely. ## If ``precision == -1``, it tries to format it nicely.
when defined(js): when defined(js):
var res: cstring var res: cstring
case format case format
@ -1961,7 +1961,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
buf {.noinit.}: array[0..2500, char] buf {.noinit.}: array[0..2500, char]
L: cint L: cint
frmtstr[0] = '%' frmtstr[0] = '%'
if precision > 0: if precision >= 0:
frmtstr[1] = '#' frmtstr[1] = '#'
frmtstr[2] = '.' frmtstr[2] = '.'
frmtstr[3] = '*' frmtstr[3] = '*'
@ -1984,7 +1984,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
# but nothing else is possible: # but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalsep if buf[i] in {'.', ','}: result[i] = decimalsep
else: result[i] = buf[i] else: result[i] = buf[i]
when defined(vcc): when defined(windows):
# VS pre 2015 violates the C standard: "The exponent always contains at # VS pre 2015 violates the C standard: "The exponent always contains at
# least two digits, and only as many more digits as necessary to # least two digits, and only as many more digits as necessary to
# represent the exponent." [C11 §7.21.6.1] # represent the exponent." [C11 §7.21.6.1]
@ -1995,7 +1995,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
result.setLen(result.len - 1) result.setLen(result.len - 1)
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16; decimalSep = '.'): string {. precision: range[-1..32] = 16; decimalSep = '.'): string {.
noSideEffect, rtl, extern: "nsu$1".} = noSideEffect, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string. ## Converts a floating point value `f` to a string.
## ##
@ -2006,16 +2006,16 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
## `precision`'s default value is the maximum number of meaningful digits ## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``float`` type. ## after the decimal point for Nim's ``float`` type.
## ##
## If ``precision == 0``, it tries to format it nicely. ## If ``precision == -1``, it tries to format it nicely.
## ##
## Examples: ## Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## let x = 123.456 ## let x = 123.456
## echo x.formatFloat() # 123.4560000000000 ## doAssert x.formatFloat() == "123.4560000000000"
## echo x.formatFloat(ffDecimal, 4) # 123.4560 ## doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
## echo x.formatFloat(ffScientific, 2) # 1.23e+02 ## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
## ##
result = formatBiggestFloat(f, format, precision, decimalSep) result = formatBiggestFloat(f, format, precision, decimalSep)
@ -2471,12 +2471,14 @@ when isMainModule:
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
doAssert wordWrap(inp, 10, false) == outp doAssert wordWrap(inp, 10, false) == outp
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
["1,0e-11", "1,0e-011"] ["1,0e-11", "1,0e-011"]
# bug #6589 # bug #6589
doAssert formatFloat(123.456, ffScientific, precision=0) in doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
["1.234560e+02", "1.234560e+002"]
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"

View file

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

View file

@ -308,11 +308,16 @@ proc `$`*(u: Uri): string =
result.add(":") result.add(":")
result.add(u.password) result.add(u.password)
result.add("@") result.add("@")
if u.hostname.endswith('/'):
result.add(u.hostname[0..^2])
else:
result.add(u.hostname) result.add(u.hostname)
if u.port.len > 0: if u.port.len > 0:
result.add(":") result.add(":")
result.add(u.port) result.add(u.port)
if u.path.len > 0: if u.path.len > 0:
if u.hostname.len > 0 and u.path[0] != '/':
result.add('/')
result.add(u.path) result.add(u.path)
if u.query.len > 0: if u.query.len > 0:
result.add("?") result.add("?")
@ -483,6 +488,34 @@ when isMainModule:
let foo = parseUri("http://localhost:9515") / "status" let foo = parseUri("http://localhost:9515") / "status"
doAssert $foo == "http://localhost:9515/status" doAssert $foo == "http://localhost:9515/status"
# bug #6649 #6652
block:
var foo = parseUri("http://example.com")
foo.hostname = "example.com"
foo.path = "baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.path = "baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com"
foo.path = "/baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.path = "/baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.port = "8000"
foo.path = "baz"
doAssert $foo == "http://example.com:8000/baz"
foo = parseUri("file:/dir/file")
foo.path = "relative"
doAssert $foo == "file:relative"
# isAbsolute tests # isAbsolute tests
block: block:
doAssert "www.google.com".parseUri().isAbsolute() == false doAssert "www.google.com".parseUri().isAbsolute() == false

View file

@ -456,6 +456,13 @@ type
WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation.
ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation. ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation.
StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
## to them. A StackTraceEntry is a single entry of the
## stack trace.
procname*: cstring ## name of the proc that is currently executing
line*: int ## line number of the proc that is currently executing
filename*: cstring ## filename of the proc that is currently executing
Exception* {.compilerproc.} = object of RootObj ## \ Exception* {.compilerproc.} = object of RootObj ## \
## Base exception class. ## Base exception class.
## ##
@ -468,7 +475,10 @@ type
msg* {.exportc: "message".}: string ## the exception's message. Not msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message ## providing an exception message
## is bad style. ## is bad style.
when defined(js):
trace: string trace: string
else:
trace: seq[StackTraceEntry]
up: ref Exception # used for stacking exceptions. Not exported! up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \ SystemError* = object of Exception ## \
@ -895,7 +905,7 @@ proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
## computes the integer division. This is roughly the same as ## computes the integer division. This is roughly the same as
## ``floor(x/y)``. ## ``trunc(x/y)``.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## 1 div 2 == 0 ## 1 div 2 == 0
@ -1097,7 +1107,7 @@ proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.} proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
## computes the integer division. This is roughly the same as ## computes the integer division. This is roughly the same as
## ``floor(x/y)``. ## ``trunc(x/y)``.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## (7 div 5) == 1 ## (7 div 5) == 1
@ -2015,19 +2025,19 @@ when defined(nimNewRoof):
yield res yield res
inc(res) inc(res)
iterator `..`*(a, b: int64): int64 {.inline.} = template dotdotImpl(t) {.dirty.} =
## A special version of ``..`` for ``int64`` only. iterator `..`*(a, b: t): t {.inline.} =
## A type specialized version of ``..`` for convenience so that
## mixing integer types work better.
var res = a var res = a
while res <= b: while res <= b:
yield res yield res
inc(res) inc(res)
iterator `..`*(a, b: int32): int32 {.inline.} = dotdotImpl(int64)
## A special version of ``..`` for ``int32`` only. dotdotImpl(int32)
var res = a dotdotImpl(uint64)
while res <= b: dotdotImpl(uint32)
yield res
inc(res)
else: else:
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
@ -2763,9 +2773,9 @@ when defined(nimvarargstyped):
## pretends to be free of side effects, so that it can be used for debugging ## pretends to be free of side effects, so that it can be used for debugging
## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_. ## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
else: else:
proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
benign, sideEffect.} benign, sideEffect.}
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].} tags: [], raises: [].}
template newException*(exceptn: typedesc, message: string; template newException*(exceptn: typedesc, message: string;
@ -3706,7 +3716,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
## .. code-block:: nim ## .. code-block:: nim
## import strutils ## import strutils
## ##
## template testException(exception, code: expr): stmt = ## template testException(exception, code: untyped): typed =
## try: ## try:
## let pos = instantiationInfo() ## let pos = instantiationInfo()
## discard(code) ## discard(code)
@ -3844,11 +3854,11 @@ type
{.deprecated: [PNimrodNode: NimNode].} {.deprecated: [PNimrodNode: NimNode].}
when false: when false:
template eval*(blk: stmt): stmt = template eval*(blk: typed): typed =
## executes a block of code at compile time just as if it was a macro ## executes a block of code at compile time just as if it was a macro
## optionally, the block can return an AST tree that will replace the ## optionally, the block can return an AST tree that will replace the
## eval expression ## eval expression
macro payload: stmt {.gensym.} = blk macro payload: typed {.gensym.} = blk
payload() payload()
when hasAlloc: when hasAlloc:

View file

@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = memLoc result = memLoc
when defined(vcc): when defined(vcc):
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 proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".} {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
{.importc: "_InterlockedCompareExchange", header: "<intrin.h>".} {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".} {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
when sizeof(T) == 8: when sizeof(T) == 8:

View file

@ -370,7 +370,7 @@ proc commandPrompt() =
if dbgUser.len == 0: dbgUser.len = oldLen if dbgUser.len == 0: dbgUser.len = oldLen
# now look what we have to do: # now look what we have to do:
var i = scanWord(addr dbgUser.data, dbgTemp, 0) var i = scanWord(addr dbgUser.data, dbgTemp, 0)
template `?`(x: expr): expr = dbgTemp == cstring(x) template `?`(x: untyped): untyped = dbgTemp == cstring(x)
if ?"s" or ?"step": if ?"s" or ?"step":
dbgState = dbStepInto dbgState = dbStepInto
again = false again = false

View file

@ -155,6 +155,52 @@ when not hasThreadSupport:
var var
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
const
reraisedFromBegin = -10
reraisedFromEnd = -100
template reraisedFrom(z): untyped =
StackTraceEntry(procname: nil, line: z, filename: nil)
proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
var
it = f
i = 0
while it != nil:
inc(i)
it = it.prev
var last = i-1
if s.isNil:
s = newSeq[StackTraceEntry](i)
else:
last = s.len + i - 1
s.setLen(last+1)
it = f
while it != nil:
s[last] = StackTraceEntry(procname: it.procname,
line: it.line,
filename: it.filename)
it = it.prev
dec last
template addFrameEntry(s, f: untyped) =
var oldLen = s.len
add(s, f.filename)
if f.line > 0:
add(s, '(')
add(s, $f.line)
add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, f.procname)
add(s, "\n")
proc `$`(s: seq[StackTraceEntry]): string =
result = newStringOfCap(2000)
for i in 0 .. s.len-1:
if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
elif s[i].line == reraisedFromEnd: result.add "]]\n"
else: addFrameEntry(result, s[i])
proc auxWriteStackTrace(f: PFrame, s: var string) = proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport: when hasThreadSupport:
var var
@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
if tempFrames[j] == nil: if tempFrames[j] == nil:
add(s, "(") add(s, "(")
add(s, $skipped) add(s, $skipped)
add(s, " calls omitted) ...") add(s, " calls omitted) ...\n")
else: else:
var oldLen = s.len addFrameEntry(s, tempFrames[j])
add(s, tempFrames[j].filename)
if tempFrames[j].line > 0:
add(s, '(')
add(s, $tempFrames[j].line)
add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, tempFrames[j].procname)
add(s, "\n")
proc stackTraceAvailable*(): bool proc stackTraceAvailable*(): bool
@ -221,6 +259,13 @@ when hasSomeStackTrace:
auxWriteStackTraceWithBacktrace(s) auxWriteStackTraceWithBacktrace(s)
else: else:
add(s, "No stack traceback available\n") add(s, "No stack traceback available\n")
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
when NimStackTrace:
auxWriteStackTrace(framePtr, s)
else:
s = nil
proc stackTraceAvailable(): bool = proc stackTraceAvailable(): bool =
when NimStackTrace: when NimStackTrace:
if framePtr == nil: if framePtr == nil:
@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} =
else: else:
endbStep() # call the debugger endbStep() # call the debugger
when false:
proc rawRaise*(e: ref Exception) =
## undocumented. Do not use.
pushCurrentException(e)
c_longjmp(excHandler.context, 1)
var onUnhandledException*: (proc (errorMsg: string) {. var onUnhandledException*: (proc (errorMsg: string) {.
nimcall.}) ## set this error \ nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception. ## handler to override the existing behaviour on an unhandled exception.
@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) =
when hasSomeStackTrace: when hasSomeStackTrace:
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)
if isNil(e.trace): rawWriteStackTrace(buf) if isNil(e.trace): rawWriteStackTrace(buf)
else: add(buf, e.trace) else: add(buf, $e.trace)
add(buf, "Error: unhandled exception: ") add(buf, "Error: unhandled exception: ")
if not isNil(e.msg): add(buf, e.msg) if not isNil(e.msg): add(buf, e.msg)
add(buf, " [") add(buf, " [")
@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename if e.name.isNil: e.name = ename
when hasSomeStackTrace: when hasSomeStackTrace:
if e.trace.isNil: if e.trace.isNil:
e.trace = ""
rawWriteStackTrace(e.trace) rawWriteStackTrace(e.trace)
elif framePtr != nil: elif framePtr != nil:
e.trace.add "[[reraised from:\n" e.trace.add reraisedFrom(reraisedFromBegin)
auxWriteStackTrace(framePtr, e.trace) auxWriteStackTrace(framePtr, e.trace)
e.trace.add "]]\n" e.trace.add reraisedFrom(reraisedFromEnd)
raiseExceptionAux(e) raiseExceptionAux(e)
proc reraiseException() {.compilerRtl.} = proc reraiseException() {.compilerRtl.} =
@ -349,10 +387,15 @@ proc getStackTrace(): string =
proc getStackTrace(e: ref Exception): string = proc getStackTrace(e: ref Exception): string =
if not isNil(e) and not isNil(e.trace): if not isNil(e) and not isNil(e.trace):
result = e.trace result = $e.trace
else: else:
result = "" result = ""
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
## Returns the attached stack trace to the exception ``e`` as
## a ``seq``. This is not yet available for the JS backend.
shallowCopy(result, e.trace)
when defined(nimRequiresNimFrame): when defined(nimRequiresNimFrame):
proc stackOverflow() {.noinline.} = proc stackOverflow() {.noinline.} =
writeStackTrace() writeStackTrace()

View file

@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
when BitsPerPage mod (sizeof(int)*8) != 0: when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
template color(c): expr = c.refCount and colorMask template color(c): untyped = c.refCount and colorMask
template setColor(c, col) = template setColor(c, col) =
c.refcount = c.refcount and not colorMask or col c.refcount = c.refcount and not colorMask or col

View file

@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
## it is not necessary to call this directly; use 'spawnX' instead. ## it is not necessary to call this directly; use 'spawnX' instead.
result = gSomeReady.event result = gSomeReady.event
proc spawn*(call: stmt) {.magic: "Spawn".} proc spawn*(call: typed) {.magic: "Spawn".}
## always spawns a new task, so that the 'call' is never executed on ## always spawns a new task, so that the 'call' is never executed on
## the calling thread. 'call' has to be proc call 'p(...)' where 'p' ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
## is gcsafe and has 'void' as the return type. ## is gcsafe and has 'void' as the return type.
template spawnX*(call: stmt) = template spawnX*(call: typed) =
## spawns a new task if a CPU core is ready, otherwise executes the ## spawns a new task if a CPU core is ready, otherwise executes the
## call in the calling thread. Usually it is advised to ## call in the calling thread. Usually it is advised to
## use 'spawn' in order to not block the producer for an unknown ## use 'spawn' in order to not block the producer for an unknown

View file

@ -322,6 +322,9 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
result.add x result.add x
proc add*(result: var string; x: float) = proc add*(result: var string; x: float) =
when nimvm:
result.add $x
else:
var buf: array[0..64, char] var buf: array[0..64, char]
when defined(nimNoArrayToCstringConversion): when defined(nimNoArrayToCstringConversion):
var n: int = c_sprintf(addr buf, "%.16g", x) var n: int = c_sprintf(addr buf, "%.16g", x)

View file

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

View file

@ -3,11 +3,22 @@ discard """
output: "Finished" output: "Finished"
""" """
import asyncdispatch import asyncdispatch, asyncnet
proc createServer(port: Port) {.async.} =
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
bindAddr(server, port)
server.listen()
while true:
let client = await server.accept()
discard await client.recvLine()
asyncCheck createServer(10335.Port)
proc f(): Future[void] {.async.} = proc f(): Future[void] {.async.} =
let s = newAsyncNativeSocket() let s = newAsyncNativeSocket()
await s.connect("example.com", 80.Port) await s.connect("localhost", 10335.Port)
await s.send("123") await s.send("123")
echo "Finished" echo "Finished"

View file

@ -86,7 +86,7 @@ proc readMessages(server: AsyncFD) {.async.} =
size = 0 size = 0
var grammString = $cstring(addr buffer) var grammString = $cstring(addr buffer)
if grammString.startswith("Message ") and if grammString.startswith("Message ") and
saddr.sin_addr.s_addr == 0x100007F: saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32):
await sendTo(server, addr grammString[0], len(grammString), await sendTo(server, addr grammString[0], len(grammString),
cast[ptr SockAddr](addr saddr), slen) cast[ptr SockAddr](addr saddr), slen)
inc(msgCount) inc(msgCount)

View file

@ -0,0 +1,33 @@
discard """
output: '''@[0, 4, 9, 1, 3, 2]
@[0, 1, 2, 3, 9]'''
"""
# bug #6724
import algorithm
type
Bar = object
bar: ref seq[int]
Foo = ref Bar
proc test(x: ref Foo) =
x.bar[].del(1)
x.bar[].sort(cmp)
proc main() =
var foo: ref Foo
new(foo)
var s = @[0, 4, 9, 1, 3, 2]
var sr: ref seq[int]
new(sr)
sr[] = s
foo[] = Foo(bar: sr)
echo($foo.bar[])
test(foo)
echo($foo.bar[])
main()

View file

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

10
tests/cpp/tcasts.nim Normal file
View file

@ -0,0 +1,10 @@
discard """
cmd: "nim cpp $file"
output: ""
"""
block: #5979
var a = 'a'
var p: pointer = cast[pointer](a)
var c = cast[char](p)
doAssert(c == 'a')

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,23 @@
discard """
errormsg: '''got (tuple of (type NimEdAppWindow, int))'''
line: 22
nimout: '''got (tuple of (type NimEdAppWindow, int))
but expected one of:
template xxx(tn: typedesc; i: int)'''
"""
type
NimEdAppWindow = ptr NimEdAppWindowObj
NimEdAppWindowObj = object
i: int
template gDefineTypeExtended*(tn: typeDesc) =
discard
gDefineTypeExtended (NimEdAppWindow)
template xxx*(tn: typeDesc, i: int) =
discard
xxx (NimEdAppWindow, 0)
# bug #6776

View file

@ -5,7 +5,8 @@ discard """
6 6
abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
145 23 145 23
3''' 3
2'''
""" """
import strutils import strutils
@ -122,3 +123,21 @@ var testTry =
PFooBase(field: 5) PFooBase(field: 5)
echo(testTry.field) echo(testTry.field)
# bug #6166
proc quo(op: proc (x: int): bool): int =
result =
if op(3):
2
else:
0
echo(
if true:
quo do (a: int) -> bool:
a mod 2 != 0
else:
quo do (a: int) -> bool:
a mod 3 != 0
)

View file

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

View file

@ -1,71 +0,0 @@
discard """
file: "tromans.nim"
output: "success"
"""
import
strutils
## Convert an integer to a Roman numeral
# See http://en.wikipedia.org/wiki/Roman_numerals for reference
proc raiseInvalidValue(msg: string) {.noreturn.} =
# Yes, we really need a shorthand for this code...
var e: ref EInvalidValue
new(e)
e.msg = msg
raise e
# I should use a class, perhaps.
# --> No. Why introduce additional state into such a simple and nice
# interface? State is evil. :D
proc RomanToDecimal(romanVal: string): int =
result = 0
var prevVal = 0
for i in countdown(romanVal.len - 1, 0):
var val = 0
case romanVal[i]
of 'I', 'i': val = 1
of 'V', 'v': val = 5
of 'X', 'x': val = 10
of 'L', 'l': val = 50
of 'C', 'c': val = 100
of 'D', 'd': val = 500
of 'M', 'm': val = 1000
else: raiseInvalidValue("Incorrect character in roman numeral! (" &
$romanVal[i] & ")")
if val >= prevVal:
inc(result, val)
else:
dec(result, val)
prevVal = val
proc DecimalToRoman(decValParam: int): string =
# Apparently numbers cannot be above 4000
# Well, they can be (using overbar or parenthesis notation)
# but I see little interest (beside coding challenge) in coding them as
# we rarely use huge Roman numeral.
const romanComposites = [
("M", 1000), ("CM", 900),
("D", 500), ("CD", 400), ("C", 100),
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
("V", 5), ("IV", 4), ("I", 1)]
if decValParam < 1 or decValParam > 3999:
raiseInvalidValue("number not representable")
result = ""
var decVal = decValParam
for key, val in items(romanComposites):
while decVal >= val:
dec(decVal, val)
result.add(key)
for i in 1..100:
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
for i in items([1238, 1777, 3830, 2401, 379, 33, 940, 3973]):
if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG"
echo "success" #OUT success

View file

@ -0,0 +1,47 @@
discard """
output: '''
9.0'''
"""
### bug #6773
{.emit: """ /*INCLUDESECTION*/
typedef double cimported;
cimported set1_imported(double x) {
return x;
}
"""}
type vfloat{.importc: "cimported".} = object
proc set1(a: float): vfloat {.importc: "set1_imported".}
converter scalar_to_vector(x: float): vfloat =
set1(x)
proc sqrt(x: vfloat): vfloat =
x
proc pow(x, y: vfloat): vfloat =
y
proc `^`(x: vfloat, exp: static[int]): vfloat =
when exp == 0:
1.0
else:
x
proc `^`(x: vfloat, exp: static[float]): vfloat =
when exp == 0.5:
sqrt(x)
else:
pow(x, exp)
proc `$`(x: vfloat): string =
let y = cast[ptr float](unsafeAddr x)
echo y[]
let x = set1(9.0)
echo x^0.5

View file

@ -1,82 +1,82 @@
discard """ discard """
nimout: ''' nimout: '''
StmtList StmtList
Ident !"foo010" Ident ident"foo010"
Call Call
Ident !"foo020" Ident ident"foo020"
Call Call
Ident !"foo030" Ident ident"foo030"
Ident !"x" Ident ident"x"
Command Command
Ident !"foo040" Ident ident"foo040"
Ident !"x" Ident ident"x"
Call Call
Ident !"foo050" Ident ident"foo050"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo060" Ident ident"foo060"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo070" Ident ident"foo070"
StrLit test StrLit test
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo080" Ident ident"foo080"
StrLit test StrLit test
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo090" Ident ident"foo090"
StrLit test StrLit test
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo100" Ident ident"foo100"
Call Call
StrLit test StrLit test
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo101" Ident ident"foo101"
Call Call
IntLit 10 IntLit 10
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo110" Ident ident"foo110"
IntLit 1 IntLit 1
Par Par
Infix Infix
Ident !"+" Ident ident"+"
IntLit 2 IntLit 2
IntLit 3 IntLit 3
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo120" Ident ident"foo120"
IntLit 1 IntLit 1
Call Call
Par Par
Infix Infix
Ident !"+" Ident ident"+"
IntLit 2 IntLit 2
IntLit 3 IntLit 3
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo130" Ident ident"foo130"
Do Do
Empty Empty
Empty Empty
@ -84,7 +84,7 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"x" Ident ident"x"
Empty Empty
Empty Empty
Empty Empty
@ -93,7 +93,7 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo140" Ident ident"foo140"
Do Do
Empty Empty
Empty Empty
@ -101,8 +101,8 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"x" Ident ident"x"
Ident !"int" Ident ident"int"
Empty Empty
Empty Empty
Empty Empty
@ -110,16 +110,16 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo150" Ident ident"foo150"
Do Do
Empty Empty
Empty Empty
Empty Empty
FormalParams FormalParams
Ident !"int" Ident ident"int"
IdentDefs IdentDefs
Ident !"x" Ident ident"x"
Ident !"int" Ident ident"int"
Empty Empty
Empty Empty
Empty Empty
@ -127,9 +127,9 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo160" Ident ident"foo160"
Call Call
Ident !"x" Ident ident"x"
Do Do
Empty Empty
Empty Empty
@ -137,7 +137,7 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"y" Ident ident"y"
Empty Empty
Empty Empty
Empty Empty
@ -146,7 +146,7 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo170" Ident ident"foo170"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
@ -155,7 +155,7 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo180" Ident ident"foo180"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
@ -167,9 +167,9 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Command Command
Ident !"foo190" Ident ident"foo190"
Call Call
Ident !"x" Ident ident"x"
Do Do
Empty Empty
Empty Empty
@ -177,7 +177,7 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"y" Ident ident"y"
Empty Empty
Empty Empty
Empty Empty
@ -190,9 +190,9 @@ StmtList
Empty Empty
Empty Empty
FormalParams FormalParams
Ident !"int" Ident ident"int"
IdentDefs IdentDefs
Ident !"z" Ident ident"z"
Empty Empty
Empty Empty
Empty Empty
@ -205,10 +205,10 @@ StmtList
Empty Empty
Empty Empty
FormalParams FormalParams
Ident !"int" Ident ident"int"
IdentDefs IdentDefs
Ident !"w" Ident ident"w"
Ident !"int" Ident ident"int"
Empty Empty
Empty Empty
Empty Empty
@ -223,10 +223,10 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
Ident !"foo200" Ident ident"foo200"
Ident !"x" Ident ident"x"
Call Call
Ident !"bar" Ident ident"bar"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
@ -236,53 +236,53 @@ StmtList
Empty Empty
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Ident !"foo210" Ident ident"foo210"
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Call Call
Ident !"foo220" Ident ident"foo220"
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Call Call
Ident !"foo230" Ident ident"foo230"
Ident !"x" Ident ident"x"
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Command Command
Ident !"foo240" Ident ident"foo240"
Ident !"x" Ident ident"x"
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Call Call
Ident !"foo250" Ident ident"foo250"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Call Call
Ident !"foo260" Ident ident"foo260"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Call Call
Ident !"foo270" Ident ident"foo270"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
@ -292,12 +292,12 @@ StmtList
Empty Empty
VarSection VarSection
IdentDefs IdentDefs
Ident !"a" Ident ident"a"
Empty Empty
Command Command
Ident !"foo280" Ident ident"foo280"
Call Call
Ident !"x" Ident ident"x"
Do Do
Empty Empty
Empty Empty
@ -305,7 +305,7 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"y" Ident ident"y"
Empty Empty
Empty Empty
Empty Empty
@ -318,40 +318,40 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Asgn Asgn
Ident !"a" Ident ident"a"
Ident !"foo290" Ident ident"foo290"
Asgn Asgn
Ident !"a" Ident ident"a"
Call Call
Ident !"foo300" Ident ident"foo300"
Asgn Asgn
Ident !"a" Ident ident"a"
Call Call
Ident !"foo310" Ident ident"foo310"
Ident !"x" Ident ident"x"
Asgn Asgn
Ident !"a" Ident ident"a"
Command Command
Ident !"foo320" Ident ident"foo320"
Ident !"x" Ident ident"x"
Asgn Asgn
Ident !"a" Ident ident"a"
Call Call
Ident !"foo330" Ident ident"foo330"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Asgn Asgn
Ident !"a" Ident ident"a"
Call Call
Ident !"foo340" Ident ident"foo340"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Asgn Asgn
Ident !"a" Ident ident"a"
Call Call
Ident !"foo350" Ident ident"foo350"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
@ -360,13 +360,13 @@ StmtList
DiscardStmt DiscardStmt
Empty Empty
Asgn Asgn
Ident !"a" Ident ident"a"
Command Command
Ident !"foo360" Ident ident"foo360"
Call Call
DotExpr DotExpr
Ident !"x" Ident ident"x"
Ident !"bar" Ident ident"bar"
Do Do
Empty Empty
Empty Empty
@ -374,7 +374,7 @@ StmtList
FormalParams FormalParams
Empty Empty
IdentDefs IdentDefs
Ident !"y" Ident ident"y"
Empty Empty
Empty Empty
Empty Empty
@ -388,20 +388,20 @@ StmtList
Empty Empty
Command Command
DotExpr DotExpr
Ident !"foo370" Ident ident"foo370"
Ident !"add" Ident ident"add"
Call Call
Ident !"quote" Ident ident"quote"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty
Call Call
DotExpr DotExpr
Ident !"foo380" Ident ident"foo380"
Ident !"add" Ident ident"add"
BracketExpr BracketExpr
Call Call
Ident !"quote" Ident ident"quote"
StmtList StmtList
DiscardStmt DiscardStmt
Empty Empty

Some files were not shown because too many files have changed in this diff Show more