fixes testament compilation

This commit is contained in:
Araq
2018-05-14 17:45:44 +02:00
41 changed files with 985 additions and 297 deletions

View File

@@ -72,6 +72,8 @@
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
now joined in ``binarySearch``. ``smartbinarySearch`` is now
deprecated.
- The `terminal` module now exports additional procs for generating ANSI color
codes as strings.
### Language additions
@@ -95,11 +97,18 @@
- ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``.
- Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for
zero-copy interoperability with ``cstring``. Compile your code with the
new switch ``--laxStrings:on`` if you need a transition period.
- The command syntax now supports keyword arguments after the first comma.
- Thread-local variables can now be declared inside procs. This implies all
the effects of the `global` pragma.
- Nim now supports `except` clause in the export statement.
### Tool changes

View File

@@ -1620,6 +1620,19 @@ proc originatingModule*(s: PSym): PSym =
proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in skProcKinds
proc isCompileTimeProc*(s: PSym): bool {.inline.} =
result = s.kind == skMacro or
s.kind == skProc and sfCompileTime in s.flags
proc requiredParams*(s: PSym): int =
# Returns the number of required params (without default values)
# XXX: Perhaps we can store this in the `offset` field of the
# symbol instead?
for i in 1 ..< s.typ.len:
if s.typ.n[i].sym.ast != nil:
return i - 1
return s.typ.len - 1
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
@@ -1676,7 +1689,7 @@ proc isException*(t: PType): bool =
var base = t
while base != nil:
if base.sym.magic == mException:
if base.sym != nil and base.sym.magic == mException:
return true
base = base.lastSon
return false

View File

@@ -69,22 +69,22 @@ proc debug*(n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} =
when compiles(c.module):
c.module.fileIdx == gProjectMainIdx
c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == gProjectMainIdx
c.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == gProjectMainIdx
m.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == gProjectMainIdx
cl.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == gProjectMainIdx
p.lex.fileIdx.int32 == gProjectMainIdx
else:
p.module.module.fileIdx == gProjectMainIdx
p.module.module.fileIdx.int32 == gProjectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == gProjectMainIdx
m.module.fileIdx.int32 == gProjectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx
L.fileIdx.int32 == gProjectMainIdx
else:
error()

View File

@@ -69,7 +69,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
# if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar
var totalParams = case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1
of nkCallKinds: n.len-1
else: 0
var

View File

@@ -70,9 +70,9 @@ proc parseLine(p: var TTmplParser) =
while j <= hi and p.x[j] == ' ': inc(j)
if p.x[0] == p.nimDirective and p.x[1] == '?':
if p.x.len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
newLine(p)
elif p.x[j] == p.nimDirective:
elif j < p.x.len and p.x[j] == p.nimDirective:
newLine(p)
inc(j)
while j <= hi and p.x[j] == ' ': inc(j)

View File

@@ -16,6 +16,13 @@ import
proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc readExceptSet*(c: PContext, n: PNode): IntSet =
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
result = initIntSet()
for i in 1 ..< n.len:
let ident = lookups.considerQuotedIdent(c.config, n[i])
result.incl(ident.id)
proc importPureEnumField*(c: PContext; s: PSym) =
var check = strTableGet(c.importTable.symbols, s.name)
if check == nil:
@@ -199,9 +206,5 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
if m != nil:
n.sons[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module
var exceptSet = initIntSet()
for i in countup(1, sonsLen(n) - 1):
let ident = lookups.considerQuotedIdent(c.config, n.sons[i])
exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet)
importAllSymbolsExcept(c, m, readExceptSet(c, n))
#importForwarded(c, m.ast, exceptSet)

View File

@@ -98,6 +98,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, f
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PSym {.procvar.} =
# this is called by the semantic checking phase
assert graph.config != nil
result = compileModule(graph, fileIdx, cache, {})
graph.addDep(s, fileIdx)
#if sfSystemModule in result.flags:

View File

@@ -8,7 +8,7 @@
#
import strutils, lexbase, streams
import "../compiler" / [ast, msgs, idents]
import ".." / [ast, msgs, idents]
from os import splitFile
type

View File

@@ -713,10 +713,17 @@ proc namedParams(p: var TParser, callee: PNode,
# progress guaranteed
exprColonEqExprListAux(p, endTok, result)
proc commandParam(p: var TParser): PNode =
proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
result = parseExpr(p)
if p.tok.tokType == tkDo:
result = postExprBlocks(p, result)
elif p.tok.tokType == tkEquals and not isFirstParam:
let lhs = result
result = newNodeP(nkExprEqExpr, p)
getTok(p)
addSon(result, lhs)
addSon(result, parseExpr(p))
isFirstParam = false
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
@@ -759,10 +766,11 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
let a = result
result = newNodeP(nkCommand, p)
addSon(result, a)
var isFirstParam = true
when true:
# progress NOT guaranteed
p.hasProgress = false
addSon result, commandParam(p)
addSon result, commandParam(p, isFirstParam)
if not p.hasProgress: break
else:
while p.tok.tokType != tkEof:
@@ -1314,17 +1322,18 @@ proc parseExprStmt(p: var TParser): PNode =
addSon(result, b)
else:
# simpleExpr parsed 'p a' from 'p a, b'?
var isFirstParam = false
if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
result = a
while true:
getTok(p)
optInd(p, result)
addSon(result, commandParam(p))
addSon(result, commandParam(p, isFirstParam))
if p.tok.tokType != tkComma: break
elif p.tok.indent < 0 and isExprStart(p):
result = newNode(nkCommand, a.info, @[a])
while true:
addSon(result, commandParam(p))
addSon(result, commandParam(p, isFirstParam))
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, result)

View File

@@ -781,7 +781,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(c, it)
incl(sym.flags, sfThread)
incl(sym.flags, {sfThread, sfGlobal})
of wDeadCodeElimUnused: discard # deprecated, dead code elim always on
of wNoForward: pragmaNoForward(c, it)
of wReorder: pragmaNoForward(c, it, sfReorder)

View File

@@ -89,6 +89,7 @@ type
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
inGenericContext*: int # > 0 if we are in a generic type
inStaticContext*: int # > 0 if we are inside a static: block
inUnrolledContext*: int # > 0 if we are unrolling a loop
compilesContextId*: int # > 0 if we are in a ``compiles`` magic
compilesContextIdGenerator*: int

View File

@@ -972,18 +972,20 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
else:
result = newSymNode(s, n.info)
of skMacro:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
(n.kind notin nkCallKinds and s.requiredParams > 0):
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
result = symChoice(c, n, s, scClosed)
else:
result = semMacroExpr(c, n, n, s, flags)
of skTemplate:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
(n.kind notin nkCallKinds and s.requiredParams > 0) or
sfCustomPragma in sym.flags:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(s, n.info)
result = symChoice(c, n, s, scClosed)
else:
result = semTemplateExpr(c, n, s, flags)
of skParam:
@@ -1785,6 +1787,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
let oldInGenericContext = c.inGenericContext
let oldInUnrolledContext = c.inUnrolledContext
let oldInGenericInst = c.inGenericInst
let oldInStaticContext = c.inStaticContext
let oldProcCon = c.p
c.generics = @[]
var err: string
@@ -1799,6 +1802,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
c.inGenericContext = oldInGenericContext
c.inUnrolledContext = oldInUnrolledContext
c.inGenericInst = oldInGenericInst
c.inStaticContext = oldInStaticContext
c.p = oldProcCon
msgs.setInfoContextLen(oldContextLen)
setLen(c.graph.owners, oldOwnerLen)
@@ -2163,9 +2167,25 @@ proc semBlock(c: PContext, n: PNode): PNode =
closeScope(c)
dec(c.p.nestedBlockCounter)
proc semExportExcept(c: PContext, n: PNode): PNode =
let moduleName = semExpr(c, n[0])
if moduleName.kind != nkSym or moduleName.sym.kind != skModule:
localError(c.config, n.info, "The export/except syntax expects a module name")
return n
let exceptSet = readExceptSet(c, n)
let exported = moduleName.sym
strTableAdd(c.module.tab, exported)
var i: TTabIter
var s = initTabIter(i, exported.tab)
while s != nil:
if s.kind in ExportableSymKinds+{skModule} and
s.name.id notin exceptSet:
strTableAdd(c.module.tab, s)
s = nextIter(i, exported.tab)
result = n
proc semExport(c: PContext, n: PNode): PNode =
var x = newNodeI(n.kind, n.info)
#let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len
for i in 0..<n.len:
let a = n.sons[i]
var o: TOverloadIter
@@ -2451,9 +2471,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkIncludeStmt:
#if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "include")
result = evalInclude(c, n)
of nkExportStmt, nkExportExceptStmt:
of nkExportStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
result = semExport(c, n)
of nkExportExceptStmt:
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
result = semExportExcept(c, n)
of nkPragmaBlock:
result = semPragmaBlock(c, n)
of nkStaticStmt:

View File

@@ -199,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode =
if s != nil:
# we need to mark all symbols:
var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
# inside regular code, bindSym resolves to the sym-choice
# nodes (see tinspectsymbol)
return sc
result.add(sc)
else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)

View File

@@ -1765,7 +1765,9 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt"
#writeStackTrace()
inc c.inStaticContext
let a = semStmt(c, n.sons[0])
dec c.inStaticContext
n.sons[0] = a
evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1)

View File

@@ -1964,7 +1964,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
inc(m.genericMatches)
if arg.typ == nil:
result = arg
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
m.inheritancePenalty > 0:
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
elif arg.typ.isEmptyContainer:
result = arg.copyTree
@@ -2034,8 +2035,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym
var best = -1
for i in countup(0, sonsLen(arg) - 1):
if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
for i in 0 ..< arg.len:
if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
skIterator, skMacro, skTemplate}:
copyCandidate(z, m)
z.callee = arg.sons[i].typ
if tfUnresolved in z.callee.flags: continue
@@ -2064,6 +2066,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
x = z
elif cmp == 0:
y = z # z is as good as x
if x.state == csEmpty:
result = nil
elif y.state == csMatch and cmpCandidates(x, y) == 0:
@@ -2072,7 +2075,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# ambiguous: more than one symbol fits!
# See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
# anyway:
if f.kind == tyExpr: result = arg
if f.kind in {tyExpr, tyStmt}: result = arg
else: result = nil
else:
# only one valid interpretation found:
@@ -2172,7 +2175,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
while a < n.len:
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
formal = m.callee.n.sons[f].sym
incl(marker, formal.position)
if container.isNil:
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))

View File

@@ -117,6 +117,7 @@ proc applyFilter(p: var TParsers, n: PNode, filename: string,
of filtReplace:
result = filterReplace(p.config, stdin, filename, n)
if f != filtNone:
assert p.config != nil
if hintCodeBegin in p.config.notes:
rawMessage(p.config, hintCodeBegin, [])
msgWriteln(p.config, result.s)
@@ -124,6 +125,7 @@ proc applyFilter(p: var TParsers, n: PNode, filename: string,
proc evalPipe(p: var TParsers, n: PNode, filename: string,
start: PLLStream): PLLStream =
assert p.config != nil
result = start
if n.kind == nkEmpty: return
if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
@@ -139,10 +141,12 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
assert config != nil
var s: PLLStream
p.skin = skinStandard
let filename = fileIdx.toFullPathConsiderDirty
var pipe = parsePipe(filename, inputstream, cache, config)
p.config() = config
if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
else: s = inputstream
case p.skin

View File

@@ -614,7 +614,7 @@ proc firstOrd*(t: PType): BiggestInt =
else:
assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
result = firstOrd(lastSon(t))
of tyOrdinal:
if t.len > 0: result = firstOrd(lastSon(t))
@@ -653,7 +653,7 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
result = lastOrd(lastSon(t))
of tyProxy: result = 0
of tyOrdinal:

View File

@@ -210,8 +210,14 @@ proc putIntoNode(n: var PNode; x: TFullReg) =
of rkInt: n.intVal = x.intVal
of rkFloat: n.floatVal = x.floatVal
of rkNode:
if nfIsRef in x.node.flags: n = x.node
else: n[] = x.node[]
if nfIsRef in x.node.flags:
n = x.node
else:
let destIsRef = nfIsRef in n.flags
n[] = x.node[]
# Ref-ness must be kept for the destination
if destIsRef:
n.flags.incl nfIsRef
of rkRegisterAddr: putIntoNode(n, x.regAddr[])
of rkNodeAddr: n[] = x.nodeAddr[][]

View File

@@ -118,7 +118,7 @@ path="$lib/pure"
# Configuration for the GNU C/C++ compiler:
@if windows:
#gcc.path = r"$nim\dist\mingw\bin"
@if gcc:
@if gcc or tcc:
tlsEmulation:on
@end
@end

View File

@@ -6298,6 +6298,9 @@ modules don't need to import a module's dependencies:
var x: MyObject
echo $x
When the exported symbol is another module, all of its definitions will
be forwarded. You can use an ``except`` list to exclude some of the symbols.
Note on paths
-----------
In module related statements, if any part of the module name /
@@ -7798,8 +7801,9 @@ Future directions:
Threadvar pragma
----------------
A global variable can be marked with the ``threadvar`` pragma; it is
a `thread-local`:idx: variable then:
A variable can be marked with the ``threadvar`` pragma, which makes it a
`thread-local`:idx: variable; Additionally, this implies all the effects
of the ``global`` pragma.
.. code-block:: nim
var checkpoints* {.threadvar.}: seq[string]

View File

@@ -385,7 +385,7 @@ type
{.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol.

View File

@@ -126,6 +126,7 @@ proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
@@ -144,6 +145,7 @@ proc newRow(L: int): Row =
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt =
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
@@ -267,6 +269,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
{.tags: [WriteDbEffect], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
result = -1

View File

@@ -62,6 +62,7 @@ type
frames*: seq[TFrame]
screen*: Screen
performance*: Performance
onpopstate*: proc (event: Event)
Frame* = ref FrameObj
FrameObj {.importc.} = object of WindowObj
@@ -175,6 +176,12 @@ type
text*: cstring
value*: cstring
TextAreaElement* = ref object of ElementObj
value*: cstring
selectionStart*, selectionEnd*: int
selectionDirection*: cstring
rows*, cols*: int
FormElement* = ref FormObj
FormObj {.importc.} = object of ElementObj
action*: cstring
@@ -446,6 +453,7 @@ type
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc dispatchEvent*(et: EventTarget, ev: Event)
# Window "methods"
proc alert*(w: Window, msg: cstring)
@@ -500,7 +508,7 @@ proc removeAttributeNode*(n, attr: Node)
proc removeChild*(n, child: Node)
proc replaceChild*(n, newNode, oldNode: Node)
proc replaceData*(n: Node, start, len: int, text: cstring)
proc scrollIntoView*(n: Node)
proc scrollIntoView*(n: Node, alignToTop: bool=true)
proc setAttribute*(n: Node, name, value: cstring)
proc setAttributeNode*(n: Node, attr: Node)
@@ -596,6 +604,7 @@ proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
proc parseInt*(s: cstring): int {.importc, nodecl.}
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
type
TEventHandlers* {.deprecated.} = EventTargetObj

View File

@@ -120,6 +120,13 @@ iterator items*[A](s: HashSet[A]): A =
for h in 0..high(s.data):
if isFilled(s.data[h].hcode): yield s.data[h].key
proc hash*[A](s: HashSet[A]): Hash =
## hashing of HashSet
assert s.isValid, "The set needs to be initialized."
for h in 0..high(s.data):
result = result xor s.data[h].hcode
result = !$result
const
growthFactor = 2
@@ -690,6 +697,13 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs:
yield s.data[h].key
proc hash*[A](s: OrderedSet[A]): Hash =
## hashing of OrderedSet
assert s.isValid, "The set needs to be initialized."
forAllOrderedPairs:
result = result !& s.data[h].hcode
result = !$result
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
assert s.isValid, "The set needs to be initialized"
forAllOrderedPairs:

View File

@@ -331,7 +331,7 @@ proc slave(w: ptr Worker) {.thread.} =
await(w.taskArrived)
# XXX Somebody needs to look into this (why does this assertion fail
# in Visual Studio?)
when not defined(vcc): assert(not w.ready)
when not defined(vcc) and not defined(tcc): assert(not w.ready)
withLock numSlavesLock:
inc numSlavesRunning

View File

@@ -960,7 +960,7 @@ when defined(posix) and not defined(nimdoc):
raise newException(ValueError, "socket path too long")
copyMem(addr result.sun_path, path.cstring, path.len + 1)
when defined(posix):
when defined(posix) or defined(nimdoc):
proc connectUnix*(socket: Socket, path: string) =
## Connects to Unix socket on `path`.
## This only works on Unix-style systems: Mac OS X, BSD and Linux

View File

@@ -1262,21 +1262,20 @@ proc initSkipTable*(a: var SkipTable, sub: string)
{.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
## Preprocess table `a` for `sub`.
let m = len(sub)
let m1 = m + 1
var i = 0
while i <= 0xff-7:
a[chr(i + 0)] = m1
a[chr(i + 1)] = m1
a[chr(i + 2)] = m1
a[chr(i + 3)] = m1
a[chr(i + 4)] = m1
a[chr(i + 5)] = m1
a[chr(i + 6)] = m1
a[chr(i + 7)] = m1
a[chr(i + 0)] = m
a[chr(i + 1)] = m
a[chr(i + 2)] = m
a[chr(i + 3)] = m
a[chr(i + 4)] = m
a[chr(i + 5)] = m
a[chr(i + 6)] = m
a[chr(i + 7)] = m
i += 8
for i in 0..m-1:
a[sub[i]] = m-i
for i in 0 ..< m - 1:
a[sub[i]] = m - 1 - i
proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int
{.noSideEffect, rtl, extern: "nsuFindStrA".} =
@@ -1284,18 +1283,29 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0):
## If `last` is unspecified, it defaults to `s.high`.
##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
let
last = if last==0: s.high else: last
m = len(sub)
n = last + 1
# search:
var j = start
while j <= n - m:
block match:
for k in 0..m-1:
if sub[k] != s[k+j]: break match
return j
inc(j, a[s[j+m]])
sLen = last - start + 1
subLast = sub.len - 1
if subLast == -1:
# this was an empty needle string,
# we count this as match in the first possible position:
return start
# This is an implementation of the Boyer-Moore Horspool algorithms
# https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
var skip = start
while last - skip >= subLast:
var i = subLast
while s[skip + i] == sub[i]:
if i == 0:
return skip
dec i
inc skip, a[s[skip + subLast]]
return -1
when not (defined(js) or defined(nimdoc) or defined(nimscript)):
@@ -1455,23 +1465,41 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`.
var a {.noinit.}: SkipTable
result = ""
initSkipTable(a, sub)
let last = s.high
var i = 0
while true:
let j = find(a, s, sub, i, last)
if j < 0: break
add result, substr(s, i, j - 1)
let subLen = sub.len
if subLen == 0:
for c in s:
add result, by
add result, c
add result, by
if sub.len == 0:
if i < s.len: add result, s[i]
i = j + 1
else:
i = j + sub.len
# copy the rest:
add result, substr(s, i)
return
elif subLen == 1:
# when the pattern is a single char, we use a faster
# char-based search that doesn't need a skip table:
var c = sub[0]
let last = s.high
var i = 0
while true:
let j = find(s, c, i, last)
if j < 0: break
add result, substr(s, i, j - 1)
add result, by
i = j + subLen
# copy the rest:
add result, substr(s, i)
else:
var a {.noinit.}: SkipTable
initSkipTable(a, sub)
let last = s.high
var i = 0
while true:
let j = find(a, s, sub, i, last)
if j < 0: break
add result, substr(s, i, j - 1)
add result, by
i = j + subLen
# copy the rest:
add result, substr(s, i)
proc replace*(s: string, sub, by: char): string {.noSideEffect,
rtl, extern: "nsuReplaceChar".} =
@@ -1492,6 +1520,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
## Each occurrence of `sub` has to be surrounded by word boundaries
## (comparable to ``\\w`` in regular expressions), otherwise it is not
## replaced.
if sub.len == 0: return s
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
var a {.noinit.}: SkipTable
result = ""
@@ -2326,236 +2355,243 @@ proc removePrefix*(s: var string, prefix: string) {.
s.delete(0, prefix.len - 1)
when isMainModule:
doAssert align("abc", 4) == " abc"
doAssert align("a", 0) == "a"
doAssert align("1232", 6) == " 1232"
doAssert align("1232", 6, '#') == "##1232"
proc nonStaticTests =
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
["1,0e-11", "1,0e-011"]
# bug #6589
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
doAssert alignLeft("abc", 4) == "abc "
doAssert alignLeft("a", 0) == "a"
doAssert alignLeft("1232", 6) == "1232 "
doAssert alignLeft("1232", 6, '#') == "1232##"
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
let
inp = """ this is a long text -- muchlongerthan10chars and here
it goes"""
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
doAssert wordWrap(inp, 10, false) == outp
block: # formatSize tests
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
doAssert formatSize(4096) == "4KiB"
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
let
longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
doAssert wordWrap(longInp, 8, true) == longOutp
block: # formatEng tests
doAssert formatEng(0, 2, trim=false) == "0.00"
doAssert formatEng(0, 2) == "0"
doAssert formatEng(53, 2, trim=false) == "53.00"
doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
doAssert formatEng(0.053, 4, trim=true) == "53e-3"
doAssert formatEng(0.053, 0) == "53e-3"
doAssert formatEng(52731234) == "52.731234e6"
doAssert formatEng(-52731234) == "-52.731234e6"
doAssert formatEng(52731234, 1) == "52.7e6"
doAssert formatEng(-52731234, 1) == "-52.7e6"
doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
["1,0e-11", "1,0e-011"]
# bug #6589
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
doAssert formatEng(4100, siPrefix=true) == "4.1 k"
doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
doAssert formatEng(4100) == "4.1e3"
doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
# Don't use SI prefix as number is too big
doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
# Don't use SI prefix as number is too small
doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
proc staticTests =
doAssert align("abc", 4) == " abc"
doAssert align("a", 0) == "a"
doAssert align("1232", 6) == " 1232"
doAssert align("1232", 6, '#') == "##1232"
block: # formatSize tests
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
doAssert formatSize(4096) == "4KiB"
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
doAssert alignLeft("abc", 4) == "abc "
doAssert alignLeft("a", 0) == "a"
doAssert alignLeft("1232", 6) == "1232 "
doAssert alignLeft("1232", 6, '#') == "1232##"
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
"The cat eats fish."
let
inp = """ this is a long text -- muchlongerthan10chars and here
it goes"""
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
doAssert wordWrap(inp, 10, false) == outp
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
let
longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
doAssert wordWrap(longInp, 8, true) == longOutp
doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc"
doAssert "oo".replace("", "abc") == "abcoabcoabc"
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
"The cat eats fish."
type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[MyEnum]("enu_D") == enuD
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
doAssert parseEnum("invalid enum value", enC) == enC
doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
doAssert "oo".replace("", "abc") == "abcoabcoabc"
doAssert center("foo", 13) == " foo "
doAssert center("foo", 0) == "foo"
doAssert center("foo", 3, fillChar = 'a') == "foo"
doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[MyEnum]("enu_D") == enuD
doAssert count("foofoofoo", "foofoo") == 1
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
doAssert count("foofoofoo", 'f') == 3
doAssert count("foofoofoobar", {'f','b'}) == 4
doAssert parseEnum("invalid enum value", enC) == enC
doAssert strip(" foofoofoo ") == "foofoofoo"
doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
doAssert strip("stripme but don't strip this stripme",
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
" but don't strip this "
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
doAssert center("foo", 13) == " foo "
doAssert center("foo", 0) == "foo"
doAssert center("foo", 3, fillChar = 'a') == "foo"
doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
doAssert count("foofoofoo", "foofoo") == 1
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
doAssert count("foofoofoo", 'f') == 3
doAssert count("foofoofoobar", {'f','b'}) == 4
doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
doAssert strip(" foofoofoo ") == "foofoofoo"
doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
doAssert strip("stripme but don't strip this stripme",
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
" but don't strip this "
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
doAssert isAlphaAscii('r')
doAssert isAlphaAscii('A')
doAssert(not isAlphaAscii('$'))
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
doAssert isAlphaAscii("Rasp")
doAssert isAlphaAscii("Args")
doAssert(not isAlphaAscii("$Tomato"))
doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
doAssert isAlphaNumeric('3')
doAssert isAlphaNumeric('R')
doAssert(not isAlphaNumeric('!'))
doAssert isAlphaAscii('r')
doAssert isAlphaAscii('A')
doAssert(not isAlphaAscii('$'))
doAssert isAlphaNumeric("34ABc")
doAssert isAlphaNumeric("Rad")
doAssert isAlphaNumeric("1234")
doAssert(not isAlphaNumeric("@nose"))
doAssert isAlphaAscii("Rasp")
doAssert isAlphaAscii("Args")
doAssert(not isAlphaAscii("$Tomato"))
doAssert isDigit('3')
doAssert(not isDigit('a'))
doAssert(not isDigit('%'))
doAssert isAlphaNumeric('3')
doAssert isAlphaNumeric('R')
doAssert(not isAlphaNumeric('!'))
doAssert isDigit("12533")
doAssert(not isDigit("12.33"))
doAssert(not isDigit("A45b"))
doAssert isAlphaNumeric("34ABc")
doAssert isAlphaNumeric("Rad")
doAssert isAlphaNumeric("1234")
doAssert(not isAlphaNumeric("@nose"))
doAssert isSpaceAscii('\t')
doAssert isSpaceAscii('\l')
doAssert(not isSpaceAscii('A'))
doAssert isDigit('3')
doAssert(not isDigit('a'))
doAssert(not isDigit('%'))
doAssert isSpaceAscii("\t\l \v\r\f")
doAssert isSpaceAscii(" ")
doAssert(not isSpaceAscii("ABc \td"))
doAssert isDigit("12533")
doAssert(not isDigit("12.33"))
doAssert(not isDigit("A45b"))
doAssert(isNilOrWhitespace(""))
doAssert(isNilOrWhitespace(" "))
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
doAssert(not isNilOrWhitespace("ABc \td"))
doAssert isSpaceAscii('\t')
doAssert isSpaceAscii('\l')
doAssert(not isSpaceAscii('A'))
doAssert isLowerAscii('a')
doAssert isLowerAscii('z')
doAssert(not isLowerAscii('A'))
doAssert(not isLowerAscii('5'))
doAssert(not isLowerAscii('&'))
doAssert isSpaceAscii("\t\l \v\r\f")
doAssert isSpaceAscii(" ")
doAssert(not isSpaceAscii("ABc \td"))
doAssert isLowerAscii("abcd")
doAssert(not isLowerAscii("abCD"))
doAssert(not isLowerAscii("33aa"))
doAssert(isNilOrWhitespace(""))
doAssert(isNilOrWhitespace(" "))
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
doAssert(not isNilOrWhitespace("ABc \td"))
doAssert isUpperAscii('A')
doAssert(not isUpperAscii('b'))
doAssert(not isUpperAscii('5'))
doAssert(not isUpperAscii('%'))
doAssert isLowerAscii('a')
doAssert isLowerAscii('z')
doAssert(not isLowerAscii('A'))
doAssert(not isLowerAscii('5'))
doAssert(not isLowerAscii('&'))
doAssert isUpperAscii("ABC")
doAssert(not isUpperAscii("AAcc"))
doAssert(not isUpperAscii("A#$"))
doAssert isLowerAscii("abcd")
doAssert(not isLowerAscii("abCD"))
doAssert(not isLowerAscii("33aa"))
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
doAssert isUpperAscii('A')
doAssert(not isUpperAscii('b'))
doAssert(not isUpperAscii('5'))
doAssert(not isUpperAscii('%'))
doAssert(unescape(r"\x013", "", "") == "\x013")
doAssert isUpperAscii("ABC")
doAssert(not isUpperAscii("AAcc"))
doAssert(not isUpperAscii("A#$"))
doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
doAssert join([1, 2, 3]) == "123"
doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
doAssert """~~!!foo
doAssert(unescape(r"\x013", "", "") == "\x013")
doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
doAssert join([1, 2, 3]) == "123"
doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
doAssert """~~!!foo
~~!!bar
~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
doAssert """~~!!foo
doAssert """~~!!foo
~~!!bar
~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
doAssert """~~foo
doAssert """~~foo
~~ bar
~~ baz""".unindent(4, "~") == "foo\n bar\n baz"
doAssert """foo
doAssert """foo
bar
baz
""".unindent(4) == "foo\nbar\nbaz\n"
doAssert """foo
doAssert """foo
bar
baz
""".unindent(2) == "foo\n bar\n baz\n"
doAssert """foo
doAssert """foo
bar
baz
""".unindent(100) == "foo\nbar\nbaz\n"
doAssert """foo
doAssert """foo
foo
bar
""".unindent() == "foo\nfoo\nbar\n"
let s = " this is an example "
let s2 = ":this;is;an:example;;"
let s = " this is an example "
let s2 = ":this;is;an:example;;"
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
block: # formatEng tests
doAssert formatEng(0, 2, trim=false) == "0.00"
doAssert formatEng(0, 2) == "0"
doAssert formatEng(53, 2, trim=false) == "53.00"
doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
doAssert formatEng(0.053, 4, trim=true) == "53e-3"
doAssert formatEng(0.053, 0) == "53e-3"
doAssert formatEng(52731234) == "52.731234e6"
doAssert formatEng(-52731234) == "-52.731234e6"
doAssert formatEng(52731234, 1) == "52.7e6"
doAssert formatEng(-52731234, 1) == "-52.7e6"
doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
block: # startsWith / endsWith char tests
var s = "abcdef"
doAssert s.startsWith('a')
doAssert s.startsWith('b') == false
doAssert s.endsWith('f')
doAssert s.endsWith('a') == false
doAssert s.endsWith('\0') == false
doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
doAssert formatEng(4100, siPrefix=true) == "4.1 k"
doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
doAssert formatEng(4100) == "4.1e3"
doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
# Don't use SI prefix as number is too big
doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
# Don't use SI prefix as number is too small
doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
#echo("strutils tests passed")
block: # startsWith / endsWith char tests
var s = "abcdef"
doAssert s.startsWith('a')
doAssert s.startsWith('b') == false
doAssert s.endsWith('f')
doAssert s.endsWith('a') == false
doAssert s.endsWith('\0') == false
nonStaticTests()
staticTests()
static: staticTests()
#echo("strutils tests passed")

View File

@@ -29,9 +29,7 @@ when not hasThreadSupport:
var
colorsFGCache = initTable[Color, string]()
colorsBGCache = initTable[Color, string]()
when not defined(windows):
var
styleCache = initTable[int, string]()
styleCache = initTable[int, string]()
var
trueColorIsSupported: bool
@@ -41,10 +39,8 @@ var
const
fgPrefix = "\x1b[38;2;"
bgPrefix = "\x1b[48;2;"
when not defined(windows):
const
stylePrefix = "\e["
ansiResetCode* = "\e[0m"
stylePrefix = "\e["
when defined(windows):
import winlean, os
@@ -468,7 +464,7 @@ proc resetAttributes*(f: File) =
else:
discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
else:
f.write("\e[0m")
f.write(ansiResetCode)
type
Style* = enum ## different styles for text output
@@ -487,15 +483,22 @@ when not defined(windows):
gFG {.threadvar.}: int
gBG {.threadvar.}: int
proc getStyleStr(style: int): string =
when hasThreadSupport:
result = fmt"{stylePrefix}{style}m"
proc ansiStyleCode*(style: int): string =
when hasThreadSupport:
result = fmt"{stylePrefix}{style}m"
else:
if styleCache.hasKey(style):
result = styleCache[style]
else:
if styleCache.hasKey(style):
result = styleCache[style]
else:
result = fmt"{stylePrefix}{style}m"
styleCache[style] = result
result = fmt"{stylePrefix}{style}m"
styleCache[style] = result
template ansiStyleCode*(style: Style): string =
ansiStyleCode(style.int)
# The styleCache can be skipped when `style` is known at compile-time
template ansiStyleCode*(style: static[Style]): string =
(static(stylePrefix & $style.int & "m"))
proc setStyle*(f: File, style: set[Style]) =
## Sets the terminal style.
@@ -510,7 +513,7 @@ proc setStyle*(f: File, style: set[Style]) =
discard setConsoleTextAttribute(h, old or a)
else:
for s in items(style):
f.write(getStyleStr(ord(s)))
f.write(ansiStyleCode(s))
proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
## Writes the text `txt` in a given `style` to stdout.
@@ -524,9 +527,9 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
stdout.write(txt)
stdout.resetAttributes()
if gFG != 0:
stdout.write(getStyleStr(gFG))
stdout.write(ansiStyleCode(gFG))
if gBG != 0:
stdout.write(getStyleStr(gBG))
stdout.write(ansiStyleCode(gBG))
type
ForegroundColor* = enum ## terminal's foreground colors
@@ -572,7 +575,7 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
else:
gFG = ord(fg)
if bright: inc(gFG, 60)
f.write(getStyleStr(gFG))
f.write(ansiStyleCode(gFG))
proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
## Sets the terminal's background color.
@@ -594,10 +597,18 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
else:
gBG = ord(bg)
if bright: inc(gBG, 60)
f.write(getStyleStr(gBG))
f.write(ansiStyleCode(gBG))
proc ansiForegroundColorCode*(fg: ForegroundColor, bright=false): string =
var style = ord(fg)
if bright: inc(style, 60)
return ansiStyleCode(style)
proc getFGColorStr(color: Color): string =
template ansiForegroundColorCode*(fg: static[ForegroundColor],
bright: static[bool] = false): string =
ansiStyleCode(fg.int + bright.int * 60)
proc ansiForegroundColorCode*(color: Color): string =
when hasThreadSupport:
let rgb = extractRGB(color)
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
@@ -609,7 +620,11 @@ proc getFGColorStr(color: Color): string =
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result
proc getBGColorStr(color: Color): string =
template ansiForegroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color)
(static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
proc ansiBackgroundColorCode*(color: Color): string =
when hasThreadSupport:
let rgb = extractRGB(color)
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
@@ -621,15 +636,19 @@ proc getBGColorStr(color: Color): string =
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result
template ansiBackgroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color)
(static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
proc setForegroundColor*(f: File, color: Color) =
## Sets the terminal's foreground true color.
if trueColorIsEnabled:
f.write(getFGColorStr(color))
f.write(ansiForegroundColorCode(color))
proc setBackgroundColor*(f: File, color: Color) =
## Sets the terminal's background true color.
if trueColorIsEnabled:
f.write(getBGColorStr(color))
f.write(ansiBackgroundColorCode(color))
proc setTrueColor(f: File, color: Color) =
if fgSetColor:

View File

@@ -1069,17 +1069,15 @@ proc splitData*(textNode: PText, offset: int): PText =
var newNode: PText = textNode.fOwnerDocument.createTextNode(right)
return newNode
# ProcessingInstruction
proc target*(pi: PProcessingInstruction): string =
## Returns the Processing Instructions target
return pi.fTarget
# --Other stuff--
# Writer
proc addEscaped(s: string): string =
proc escapeXml*(s: string; result: var string) =
## Prepares a string for insertion into a XML document
## by escaping the XML special characters.
result = ""
for c in items(s):
case c
@@ -1089,11 +1087,20 @@ proc addEscaped(s: string): string =
of '"': result.add("&quot;")
else: result.add(c)
proc escapeXml*(s: string): string =
## Prepares a string for insertion into a XML document
## by escaping the XML special characters.
result = newStringOfCap(s.len + s.len shr 4)
escapeXml(s, result)
# --Other stuff--
# Writer
proc nodeToXml(n: PNode, indent: int = 0): string =
result = spaces(indent) & "<" & n.nodeName
if not isNil(n.attributes):
for i in items(n.attributes):
result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
result.add(" " & i.name & "=\"" & escapeXml(i.value) & "\"")
if isNil(n.childNodes) or n.childNodes.len() == 0:
result.add("/>") # No idea why this doesn't need a \n :O
@@ -1106,7 +1113,7 @@ proc nodeToXml(n: PNode, indent: int = 0): string =
result.add(nodeToXml(i, indent + 2))
of TextNode:
result.add(spaces(indent * 2))
result.add(addEscaped(i.nodeValue))
result.add(escapeXml(i.nodeValue))
of CDataSectionNode:
result.add(spaces(indent * 2))
result.add("<![CDATA[" & i.nodeValue & "]]>")

View File

@@ -241,7 +241,7 @@ when defined(vcc):
else:
{.error: "invalid CAS instruction".}
elif defined(tcc) and not defined(windows):
elif defined(tcc):
when defined(amd64):
{.emit:"""
static int __tcc_cas(int *ptr, int oldVal, int newVal)
@@ -262,7 +262,7 @@ static int __tcc_cas(int *ptr, int oldVal, int newVal)
}
""".}
else:
assert sizeof(int) == 4
#assert sizeof(int) == 4
{.emit:"""
static int __tcc_cas(int *ptr, int oldVal, int newVal)
{

View File

@@ -1,4 +1,6 @@
import sets
import hashes
import algorithm
block setEquality:
var
@@ -35,7 +37,7 @@ block setWithSequences:
doAssert( not s.contains(@[4, 5, 6]) )
block setClearWorked:
var s = initSet[char]()
var s = initSet[char]()
for c in "this is a test":
s.incl(c)
@@ -68,12 +70,54 @@ block orderedSetClearWorked:
for c in "eat at joes":
s.incl(c)
r = ""
r = ""
for c in items(s):
add(r, c)
doAssert r == "zeat jos"
block hashForHashedSet:
let
seq1 = "This is the test."
seq2 = "the test is This."
s1 = seq1.toSet()
s2 = seq2.toSet()
var hashSeq: seq[Hash] = @[]
doAssert s1 == s2
doAssert hash(s1) == hash(s2)
block hashForOrderdSet:
let
str = "This is the test."
rstr = str.reversed
var
s1 = initOrderedSet[char]()
s2 = initOrderedSet[char]()
r = initOrderedSet[char]()
expected: Hash
added: seq[char] = @[]
reversed: Hash
radded: seq[char] = @[]
expected = 0
for c in str:
if (not (c in added)):
expected = expected !& hash(c)
added.add(c)
s1.incl(c)
s2.incl(c)
expected = !$expected
doAssert hash(s1) == expected
doAssert hash(s1) == hash(s2)
doAssert hash(s1) != hash(r)
reversed = 0
for c in rstr:
if (not (c in radded)):
reversed = reversed !& hash(c)
radded.add(c)
r.incl(c)
reversed = !$reversed
doAssert hash(r) == reversed
doAssert hash(s1) != reversed

View File

@@ -0,0 +1,14 @@
type
Base[T] = ref object {.inheritable.}
value*: T
Derived[T] = ref object of Base[T]
derivedValue*: T
proc makeDerived*[T](v: T): Derived[T] =
new result
result.value = v
proc setBaseValue*[T](a: Base[T], value: T) =
a.value = value

View File

@@ -1,10 +1,11 @@
discard """
output: "seq[float]\n0"
targets: "c cpp"
"""
# https://github.com/nim-lang/Nim/issues/5602
import typetraits
import typetraits, module_with_generics
type
Foo[T] = object of RootObj
@@ -16,3 +17,8 @@ proc p[T](f: Foo[T]): T =
var s: Bar[float]
echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead
# Test overloading and code generation when
# downcasting is required for generic types:
var d = makeDerived(10)
setBaseValue(d, 20)

View File

@@ -0,0 +1,154 @@
discard """
output: '''
main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20
exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16
'''
"""
import macros, tables
template scopeHolder =
0 # scope revision number
type
BindingsSet = Table[string, NimNode]
proc actualBody(n: NimNode): NimNode =
# skip over the double StmtList node introduced in `mergeScopes`
result = n.body
if result.kind == nnkStmtList and result[0].kind == nnkStmtList:
result = result[0]
iterator bindings(n: NimNode, skip = 0): (string, NimNode) =
for i in skip ..< n.len:
let child = n[i]
if child.kind in {nnkAsgn, nnkExprEqExpr}:
let name = $child[0]
let value = child[1]
yield (name, value)
proc scopeRevision(scopeHolder: NimNode): int =
# get the revision number from a scopeHolder sym
assert scopeHolder.kind == nnkSym
var revisionNode = scopeHolder.getImpl.actualBody[0]
result = int(revisionNode.intVal)
proc lastScopeHolder(scopeHolders: NimNode): NimNode =
# get the most recent scopeHolder from a symChoice node
if scopeHolders.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
var bestScopeRev = 0
assert scopeHolders.len > 0
for scope in scopeHolders:
let rev = scope.scopeRevision
if result == nil or rev > bestScopeRev:
result = scope
bestScopeRev = rev
else:
result = scopeHolders
assert result.kind == nnkSym
macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped =
var
bestScope = scopeHolders.lastScopeHolder
bestScopeRev = bestScope.scopeRevision
var finalBindings = initTable[string, NimNode]()
for k, v in bindings(bestScope.getImpl.actualBody, skip = 1):
finalBindings[k] = v
for k, v in bindings(newBindings):
finalBindings[k] = v
var newScopeDefinition = newStmtList(newLit(bestScopeRev + 1))
for k, v in finalBindings:
newScopeDefinition.add newAssignment(newIdentNode(k), v)
result = quote:
template scopeHolder = `newScopeDefinition`
template scope(newBindings: untyped) {.dirty.} =
mergeScopes(bindSym"scopeHolder", newBindings)
type
TextLogRecord = object
line: string
StdoutLogRecord = object
template setProperty(r: var TextLogRecord, key: string, val: string, isFirst: bool) =
if not first: r.line.add ", "
r.line.add key
r.line.add "="
r.line.add val
template setEventName(r: var StdoutLogRecord, name: string) =
stdout.write(name & ": ")
template setProperty(r: var StdoutLogRecord, key: string, val: auto, isFirst: bool) =
when not isFirst: stdout.write ", "
stdout.write key
stdout.write "="
stdout.write $val
template flushRecord(r: var StdoutLogRecord) =
stdout.write "\n"
stdout.flushFile
macro logImpl(scopeHolders: typed,
logStmtProps: varargs[untyped]): untyped =
let lexicalScope = scopeHolders.lastScopeHolder.getImpl.actualBody
var finalBindings = initOrderedTable[string, NimNode]()
for k, v in bindings(lexicalScope, skip = 1):
finalBindings[k] = v
for k, v in bindings(logStmtProps, skip = 1):
finalBindings[k] = v
finalBindings.sort(system.cmp)
let eventName = logStmtProps[0]
assert eventName.kind in {nnkStrLit}
let record = genSym(nskVar, "record")
result = quote:
var `record`: StdoutLogRecord
setEventName(`record`, `eventName`)
var isFirst = true
for k, v in finalBindings:
result.add newCall(newIdentNode"setProperty",
record, newLit(k), v, newLit(isFirst))
isFirst = false
result.add newCall(newIdentNode"flushRecord", record)
template log(props: varargs[untyped]) {.dirty.} =
logImpl(bindSym"scopeHolder", props)
scope:
a = 12
b = "original-b"
scope:
x = 16
b = "overriden-b"
scope:
c = 100
proc main =
scope:
c = 10
scope:
z = 20
log("main started", a = 10, b = "inner-b", d = "some-d")
main()
log("exiting", msg = "bye bye")

View File

@@ -0,0 +1,173 @@
import
macros, algorithm, strutils
proc normalProc(x: int) =
echo x
template templateWithtouParams =
echo 10
proc overloadedProc(x: int) =
echo x
proc overloadedProc(x: string) =
echo x
proc overloadedProc[T](x: T) =
echo x
template normalTemplate(x: int) =
echo x
template overloadedTemplate(x: int) =
echo x
template overloadedTemplate(x: string) =
echo x
macro normalMacro(x: int): untyped =
discard
macro macroWithoutParams: untyped =
discard
macro inspectSymbol(sym: typed, expected: static[string]): untyped =
if sym.kind == nnkSym:
echo "Symbol node:"
let res = sym.getImpl.repr & "\n"
echo res
# echo "|", res, "|"
# echo "|", expected, "|"
if expected.len > 0: assert res == expected
elif sym.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
echo "Begin sym choice:"
var results = newSeq[string](0)
for innerSym in sym:
results.add innerSym.getImpl.repr
sort(results, cmp[string])
let res = results.join("\n") & "\n"
echo res
if expected.len > 0: assert res == expected
echo "End symchoice."
else:
echo "Non-symbol node: ", sym.kind
if expected.len > 0: assert $sym.kind == expected
macro inspectUntyped(sym: untyped, expected: static[string]): untyped =
let res = sym.repr
echo "Untyped node: ", res
assert res == expected
inspectSymbol templateWithtouParams, "nnkCommand"
# this template is expanded, because bindSym was not used
# the end result is the template body (nnkCommand)
inspectSymbol bindSym("templateWithtouParams"), """
template templateWithtouParams() =
echo 10
"""
inspectSymbol macroWithoutParams, "nnkEmpty"
# Just like the template above, the macro was expanded
inspectSymbol bindSym("macroWithoutParams"), """
macro macroWithoutParams(): untyped =
discard
"""
inspectSymbol normalMacro, """
macro normalMacro(x: int): untyped =
discard
"""
# Since the normalMacro has params, it's automatically
# treated as a symbol here (no need for `bindSym`)
inspectSymbol bindSym("normalMacro"), """
macro normalMacro(x: int): untyped =
discard
"""
inspectSymbol normalTemplate, """
template normalTemplate(x: int) =
echo x
"""
inspectSymbol bindSym("normalTemplate"), """
template normalTemplate(x: int) =
echo x
"""
inspectSymbol overloadedTemplate, """
template overloadedTemplate(x: int) =
echo x
template overloadedTemplate(x: string) =
echo x
"""
inspectSymbol bindSym("overloadedTemplate"), """
template overloadedTemplate(x: int) =
echo x
template overloadedTemplate(x: string) =
echo x
"""
inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")"""
# binSym is active only in the presense of `typed` params.
# `untyped` params still get the raw AST
inspectSymbol normalProc, """
proc normalProc(x: int) =
echo [x]
"""
inspectSymbol bindSym("normalProc"), """
proc normalProc(x: int) =
echo [x]
"""
inspectSymbol overloadedProc, """
proc overloadedProc(x: int) =
echo [x]
proc overloadedProc(x: string) =
echo [x]
proc overloadedProc[T](x: T) =
echo x
"""
# XXX: There seems to be a repr rendering problem above.
# Notice that `echo [x]`
inspectSymbol overloadedProc[float], """
proc overloadedProc(x: T) =
echo [x]
"""
# As expected, when we select a specific generic, the
# AST is no longer a symChoice
inspectSymbol bindSym("overloadedProc"), """
proc overloadedProc(x: int) =
echo [x]
proc overloadedProc(x: string) =
echo [x]
proc overloadedProc[T](x: T) =
echo x
"""

View File

@@ -0,0 +1,4 @@
var v*: int
proc p* = echo "proc p called"
template t* = echo "template t expanded"

View File

@@ -0,0 +1,3 @@
import definitions
export definitions except p

View File

@@ -6,6 +6,10 @@ output: '''baz
a
b
c
x: 1, y: test 1
x: 2, y: test 2
x: 10, y: test 3
x: 4, y: test 4
'''
"""
@@ -35,3 +39,15 @@ templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1
procForwarding "a", "b", "c"
proc hasKeywordArgs(x = 10, y = "y") =
echo "x: ", x, ", y: ", y
proc hasRegularArgs(x: int, y: string) =
echo "x: ", x, ", y: ", y
templateForwarding(hasRegularArgs, true, 1, "test 1")
templateForwarding hasKeywordArgs, true, 2, "test 2"
templateForwarding(hasKeywordArgs, true, y = "test 3")
templateForwarding hasKeywordArgs, true, y = "test 4", x = 4

View File

@@ -0,0 +1,78 @@
discard """
output: '''
10
1111
1222
3030303
3060606
6060606
6121212
3030903
3061206
3031503
3061806
5050505
5101010
'''
"""
import typetraits
var tls1 {.threadvar.}: int
var g0: int
var g1 {.global.}: int
proc customInc(x: var int, delta: int) =
x += delta
customInc(tls1, 10)
echo tls1
proc nonGenericProc: int =
var local: int
var nonGenericTls {.threadvar.}: int
var nonGenericGlobal {.global.}: int
var nonGenericMixedPragmas {.global, threadvar.}: int
customInc local, 1000
customInc nonGenericTls, 1
customInc nonGenericGlobal, 10
customInc nonGenericMixedPragmas, 100
return local + nonGenericTls + nonGenericGlobal + nonGenericMixedPragmas
proc genericProc(T: typedesc): int =
var local: int
var genericTls {.threadvar.}: int
var genericGlobal {.global.}: int
var genericMixedPragmas {.global, threadvar.}: int
customInc local, T.name.len * 1000000
customInc genericTls, T.name.len * 1
customInc genericGlobal, T.name.len * 100
customInc genericMixedPragmas, T.name.len * 10000
return local + genericTls + genericGlobal + genericMixedPragmas
echo nonGenericProc()
echo nonGenericProc()
echo genericProc(int)
echo genericProc(int)
echo genericProc(string)
echo genericProc(string)
proc echoInThread[T]() {.thread.} =
echo genericProc(T)
echo genericProc(T)
proc newEchoThread(T: typedesc) =
var t: Thread[void]
createThread(t, echoInThread[T])
joinThreads(t)
newEchoThread int
newEchoThread int
newEchoThread float

12
tests/vm/tref.nim Normal file
View File

@@ -0,0 +1,12 @@
static:
var
a: ref string
b: ref string
new a
a[] = "Hello world"
b = a
b[5] = 'c'
doAssert a[] == "Hellocworld"
doAssert b[] == "Hellocworld"

View File

@@ -187,12 +187,14 @@ when defined(windows):
proc main() =
when defined(windows):
let desiredPath = expandFilename(getCurrentDir() / "bin")
let nimDesiredPath = expandFilename(getCurrentDir() / "bin")
let nimbleDesiredPath = expandFilename(getEnv("USERPROFILE") / ".nimble" / "bin")
let p = tryGetUnicodeValue(r"Environment", "Path",
HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue(
r"System\CurrentControlSet\Control\Session Manager\Environment", "Path",
HKEY_LOCAL_MACHINE)
var alreadyInPath = false
var nimAlreadyInPath = false
var nimbleAlreadyInPath = false
var mingWchoices: seq[string] = @[]
var incompat: seq[string] = @[]
for x in p.split(';'):
@@ -200,18 +202,29 @@ proc main() =
let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
substr(x, 1, x.len-2) else: x)
except: ""
if y.cmpIgnoreCase(desiredPath) == 0: alreadyInPath = true
if y.toLowerAscii.contains("mingw"):
if y.cmpIgnoreCase(nimDesiredPath) == 0:
nimAlreadyInPath = true
elif y.cmpIgnoreCase(nimbleDesiredPath) == 0:
nimbleAlreadyInPath = true
elif y.toLowerAscii.contains("mingw"):
if dirExists(y):
if checkGccArch(y): mingWchoices.add y
else: incompat.add y
if alreadyInPath:
echo "bin/nim.exe is already in your PATH [Skipping]"
if nimAlreadyInPath:
echo "bin\\nim.exe is already in your PATH [Skipping]"
else:
if askBool("nim.exe is not in your PATH environment variable.\n" &
"Should it be added permanently? (y/n) "):
addToPathEnv(desiredPath)
addToPathEnv(nimDesiredPath)
if nimbleAlreadyInPath:
echo nimbleDesiredPath & " is already in your PATH [Skipping]"
else:
if askBool(nimbleDesiredPath & " is not in your PATH environment variable.\n" &
"Should it be added permanently? (y/n) "):
addToPathEnv(nimbleDesiredPath)
if mingWchoices.len == 0:
# No mingw in path, so try a few locations:
let alternative = tryDirs(incompat, defaultMingwLocations())