fixes testament compilation

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

View file

@ -72,6 +72,8 @@
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is - ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
now joined in ``binarySearch``. ``smartbinarySearch`` is now now joined in ``binarySearch``. ``smartbinarySearch`` is now
deprecated. deprecated.
- The `terminal` module now exports additional procs for generating ANSI color
codes as strings.
### Language additions ### Language additions
@ -95,11 +97,18 @@
- ``nil`` for strings/seqs is finally gone. Instead the default value for - ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``. these is ``"" / @[]``.
- Accessing the binary zero terminator in Nim's native strings - Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for is now invalid. Internally a Nim string still has the trailing zero for
zero-copy interoperability with ``cstring``. Compile your code with the zero-copy interoperability with ``cstring``. Compile your code with the
new switch ``--laxStrings:on`` if you need a transition period. 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 ### Tool changes

View file

@ -1620,6 +1620,19 @@ proc originatingModule*(s: PSym): PSym =
proc isRoutine*(s: PSym): bool {.inline.} = proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in skProcKinds 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.} = proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
@ -1676,7 +1689,7 @@ proc isException*(t: PType): bool =
var base = t var base = t
while base != nil: while base != nil:
if base.sym.magic == mException: if base.sym != nil and base.sym.magic == mException:
return true return true
base = base.lastSon base = base.lastSon
return false return false

View file

@ -69,22 +69,22 @@ proc debug*(n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} = template mdbg*: bool {.dirty.} =
when compiles(c.module): when compiles(c.module):
c.module.fileIdx == gProjectMainIdx c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(c.c.module): elif compiles(c.c.module):
c.c.module.fileIdx == gProjectMainIdx c.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(m.c.module): elif compiles(m.c.module):
m.c.module.fileIdx == gProjectMainIdx m.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(cl.c.module): elif compiles(cl.c.module):
cl.c.module.fileIdx == gProjectMainIdx cl.c.module.fileIdx.int32 == gProjectMainIdx
elif compiles(p): elif compiles(p):
when compiles(p.lex): when compiles(p.lex):
p.lex.fileIdx == gProjectMainIdx p.lex.fileIdx.int32 == gProjectMainIdx
else: else:
p.module.module.fileIdx == gProjectMainIdx p.module.module.fileIdx.int32 == gProjectMainIdx
elif compiles(m.module.fileIdx): elif compiles(m.module.fileIdx):
m.module.fileIdx == gProjectMainIdx m.module.fileIdx.int32 == gProjectMainIdx
elif compiles(L.fileIdx): elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx L.fileIdx.int32 == gProjectMainIdx
else: else:
error() 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 ``()`` # if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar # `n` is then a nkSym or something similar
var totalParams = case n.kind var totalParams = case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1 of nkCallKinds: n.len-1
else: 0 else: 0
var var

View file

@ -70,9 +70,9 @@ proc parseLine(p: var TTmplParser) =
while j <= hi and p.x[j] == ' ': inc(j) 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) newLine(p)
elif p.x[j] == p.nimDirective: elif j < p.x.len and p.x[j] == p.nimDirective:
newLine(p) newLine(p)
inc(j) inc(j)
while j <= hi and p.x[j] == ' ': 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 evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(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) = proc importPureEnumField*(c: PContext; s: PSym) =
var check = strTableGet(c.importTable.symbols, s.name) var check = strTableGet(c.importTable.symbols, s.name)
if check == nil: if check == nil:
@ -199,9 +206,5 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
if m != nil: if m != nil:
n.sons[0] = newSymNode(m) n.sons[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module addDecl(c, m, n.info) # add symbol to symbol table of module
var exceptSet = initIntSet() importAllSymbolsExcept(c, m, readExceptSet(c, n))
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)
#importForwarded(c, m.ast, exceptSet) #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; proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PSym {.procvar.} = cache: IdentCache): PSym {.procvar.} =
# this is called by the semantic checking phase # this is called by the semantic checking phase
assert graph.config != nil
result = compileModule(graph, fileIdx, cache, {}) result = compileModule(graph, fileIdx, cache, {})
graph.addDep(s, fileIdx) graph.addDep(s, fileIdx)
#if sfSystemModule in result.flags: #if sfSystemModule in result.flags:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -199,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode =
if s != nil: if s != nil:
# we need to mark all symbols: # we need to mark all symbols:
var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) 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) result.add(sc)
else: else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) 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 = proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt" #echo "semStaticStmt"
#writeStackTrace() #writeStackTrace()
inc c.inStaticContext
let a = semStmt(c, n.sons[0]) let a = semStmt(c, n.sons[0])
dec c.inStaticContext
n.sons[0] = a n.sons[0] = a
evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner) evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1) result = newNodeI(nkDiscardStmt, n.info, 1)

View file

@ -1964,7 +1964,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
inc(m.genericMatches) inc(m.genericMatches)
if arg.typ == nil: if arg.typ == nil:
result = arg 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) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
elif arg.typ.isEmptyContainer: elif arg.typ.isEmptyContainer:
result = arg.copyTree result = arg.copyTree
@ -2034,8 +2035,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
y.calleeSym = m.calleeSym y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym z.calleeSym = m.calleeSym
var best = -1 var best = -1
for i in countup(0, sonsLen(arg) - 1): for i in 0 ..< arg.len:
if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
skIterator, skMacro, skTemplate}:
copyCandidate(z, m) copyCandidate(z, m)
z.callee = arg.sons[i].typ z.callee = arg.sons[i].typ
if tfUnresolved in z.callee.flags: continue if tfUnresolved in z.callee.flags: continue
@ -2064,6 +2066,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
x = z x = z
elif cmp == 0: elif cmp == 0:
y = z # z is as good as x y = z # z is as good as x
if x.state == csEmpty: if x.state == csEmpty:
result = nil result = nil
elif y.state == csMatch and cmpCandidates(x, y) == 0: 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! # ambiguous: more than one symbol fits!
# See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
# anyway: # anyway:
if f.kind == tyExpr: result = arg if f.kind in {tyExpr, tyStmt}: result = arg
else: result = nil else: result = nil
else: else:
# only one valid interpretation found: # 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 var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
while a < n.len: 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) incl(marker, formal.position)
if container.isNil: if container.isNil:
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) 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: of filtReplace:
result = filterReplace(p.config, stdin, filename, n) result = filterReplace(p.config, stdin, filename, n)
if f != filtNone: if f != filtNone:
assert p.config != nil
if hintCodeBegin in p.config.notes: if hintCodeBegin in p.config.notes:
rawMessage(p.config, hintCodeBegin, []) rawMessage(p.config, hintCodeBegin, [])
msgWriteln(p.config, result.s) 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, proc evalPipe(p: var TParsers, n: PNode, filename: string,
start: PLLStream): PLLStream = start: PLLStream): PLLStream =
assert p.config != nil
result = start result = start
if n.kind == nkEmpty: return if n.kind == nkEmpty: return
if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|": 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; proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) = cache: IdentCache; config: ConfigRef) =
assert config != nil
var s: PLLStream var s: PLLStream
p.skin = skinStandard p.skin = skinStandard
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
var pipe = parsePipe(filename, inputstream, cache, config) var pipe = parsePipe(filename, inputstream, cache, config)
p.config() = config
if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
else: s = inputstream else: s = inputstream
case p.skin case p.skin

View file

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

View file

@ -210,8 +210,14 @@ proc putIntoNode(n: var PNode; x: TFullReg) =
of rkInt: n.intVal = x.intVal of rkInt: n.intVal = x.intVal
of rkFloat: n.floatVal = x.floatVal of rkFloat: n.floatVal = x.floatVal
of rkNode: of rkNode:
if nfIsRef in x.node.flags: n = x.node if nfIsRef in x.node.flags:
else: n[] = x.node[] 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 rkRegisterAddr: putIntoNode(n, x.regAddr[])
of rkNodeAddr: n[] = x.nodeAddr[][] of rkNodeAddr: n[] = x.nodeAddr[][]

View file

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

View file

@ -6298,6 +6298,9 @@ modules don't need to import a module's dependencies:
var x: MyObject var x: MyObject
echo $x 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 Note on paths
----------- -----------
In module related statements, if any part of the module name / In module related statements, if any part of the module name /
@ -7798,8 +7801,9 @@ Future directions:
Threadvar pragma Threadvar pragma
---------------- ----------------
A global variable can be marked with the ``threadvar`` pragma; it is A variable can be marked with the ``threadvar`` pragma, which makes it a
a `thread-local`:idx: variable then: `thread-local`:idx: variable; Additionally, this implies all the effects
of the ``global`` pragma.
.. code-block:: nim .. code-block:: nim
var checkpoints* {.threadvar.}: seq[string] var checkpoints* {.threadvar.}: seq[string]

View file

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

View file

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

View file

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

View file

@ -120,6 +120,13 @@ iterator items*[A](s: HashSet[A]): A =
for h in 0..high(s.data): for h in 0..high(s.data):
if isFilled(s.data[h].hcode): yield s.data[h].key 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 const
growthFactor = 2 growthFactor = 2
@ -690,6 +697,13 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs: forAllOrderedPairs:
yield s.data[h].key 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] = iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
assert s.isValid, "The set needs to be initialized" assert s.isValid, "The set needs to be initialized"
forAllOrderedPairs: forAllOrderedPairs:

View file

@ -331,7 +331,7 @@ proc slave(w: ptr Worker) {.thread.} =
await(w.taskArrived) await(w.taskArrived)
# XXX Somebody needs to look into this (why does this assertion fail # XXX Somebody needs to look into this (why does this assertion fail
# in Visual Studio?) # 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: withLock numSlavesLock:
inc numSlavesRunning inc numSlavesRunning

View file

@ -960,7 +960,7 @@ when defined(posix) and not defined(nimdoc):
raise newException(ValueError, "socket path too long") raise newException(ValueError, "socket path too long")
copyMem(addr result.sun_path, path.cstring, path.len + 1) 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) = proc connectUnix*(socket: Socket, path: string) =
## Connects to Unix socket on `path`. ## Connects to Unix socket on `path`.
## This only works on Unix-style systems: Mac OS X, BSD and Linux ## 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".} = {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
## Preprocess table `a` for `sub`. ## Preprocess table `a` for `sub`.
let m = len(sub) let m = len(sub)
let m1 = m + 1
var i = 0 var i = 0
while i <= 0xff-7: while i <= 0xff-7:
a[chr(i + 0)] = m1 a[chr(i + 0)] = m
a[chr(i + 1)] = m1 a[chr(i + 1)] = m
a[chr(i + 2)] = m1 a[chr(i + 2)] = m
a[chr(i + 3)] = m1 a[chr(i + 3)] = m
a[chr(i + 4)] = m1 a[chr(i + 4)] = m
a[chr(i + 5)] = m1 a[chr(i + 5)] = m
a[chr(i + 6)] = m1 a[chr(i + 6)] = m
a[chr(i + 7)] = m1 a[chr(i + 7)] = m
i += 8 i += 8
for i in 0..m-1: for i in 0 ..< m - 1:
a[sub[i]] = m-i a[sub[i]] = m - 1 - i
proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int
{.noSideEffect, rtl, extern: "nsuFindStrA".} = {.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`. ## If `last` is unspecified, it defaults to `s.high`.
## ##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
let let
last = if last==0: s.high else: last last = if last==0: s.high else: last
m = len(sub) sLen = last - start + 1
n = last + 1 subLast = sub.len - 1
# search:
var j = start if subLast == -1:
while j <= n - m: # this was an empty needle string,
block match: # we count this as match in the first possible position:
for k in 0..m-1: return start
if sub[k] != s[k+j]: break match
return j # This is an implementation of the Boyer-Moore Horspool algorithms
inc(j, a[s[j+m]]) # 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 return -1
when not (defined(js) or defined(nimdoc) or defined(nimscript)): when not (defined(js) or defined(nimdoc) or defined(nimscript)):
@ -1455,8 +1465,30 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
proc replace*(s, sub: string, by = ""): string {.noSideEffect, proc replace*(s, sub: string, by = ""): string {.noSideEffect,
rtl, extern: "nsuReplaceStr".} = rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`. ## Replaces `sub` in `s` by the string `by`.
var a {.noinit.}: SkipTable
result = "" result = ""
let subLen = sub.len
if subLen == 0:
for c in s:
add result, by
add result, c
add result, by
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) initSkipTable(a, sub)
let last = s.high let last = s.high
var i = 0 var i = 0
@ -1465,11 +1497,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
if j < 0: break if j < 0: break
add result, substr(s, i, j - 1) add result, substr(s, i, j - 1)
add result, by add result, by
if sub.len == 0: i = j + subLen
if i < s.len: add result, s[i]
i = j + 1
else:
i = j + sub.len
# copy the rest: # copy the rest:
add result, substr(s, i) add result, substr(s, i)
@ -1492,6 +1520,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
## Each occurrence of `sub` has to be surrounded by word boundaries ## Each occurrence of `sub` has to be surrounded by word boundaries
## (comparable to ``\\w`` in regular expressions), otherwise it is not ## (comparable to ``\\w`` in regular expressions), otherwise it is not
## replaced. ## replaced.
if sub.len == 0: return s
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
var a {.noinit.}: SkipTable var a {.noinit.}: SkipTable
result = "" result = ""
@ -2326,6 +2355,57 @@ proc removePrefix*(s: var string, prefix: string) {.
s.delete(0, prefix.len - 1) s.delete(0, prefix.len - 1)
when isMainModule: when isMainModule:
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 "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
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"
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 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"
proc staticTests =
doAssert align("abc", 4) == " abc" doAssert align("abc", 4) == " abc"
doAssert align("a", 0) == "a" doAssert align("a", 0) == "a"
doAssert align("1232", 6) == " 1232" doAssert align("1232", 6) == " 1232"
@ -2347,33 +2427,13 @@ when isMainModule:
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
doAssert wordWrap(longInp, 8, true) == longOutp doAssert wordWrap(longInp, 8, true) == longOutp
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 "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
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 "$animal eats $food." % ["animal", "The cat", "food", "fish"] == doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
"The cat eats fish." "The cat eats fish."
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc" doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
doAssert "oo".replace("", "abc") == "abcoabcoabc" doAssert "oo".replace("", "abc") == "abcoabcoabc"
type MyEnum = enum enA, enB, enC, enuD, enE type MyEnum = enum enA, enB, enC, enuD, enE
@ -2521,35 +2581,6 @@ bar
doAssert s.splitWhitespace(maxsplit=3) == @["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(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"
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"
block: # startsWith / endsWith char tests block: # startsWith / endsWith char tests
var s = "abcdef" var s = "abcdef"
doAssert s.startsWith('a') doAssert s.startsWith('a')
@ -2559,3 +2590,8 @@ bar
doAssert s.endsWith('\0') == false doAssert s.endsWith('\0') == false
#echo("strutils tests passed") #echo("strutils tests passed")
nonStaticTests()
staticTests()
static: staticTests()

View file

@ -29,8 +29,6 @@ when not hasThreadSupport:
var var
colorsFGCache = initTable[Color, string]() colorsFGCache = initTable[Color, string]()
colorsBGCache = initTable[Color, string]() colorsBGCache = initTable[Color, string]()
when not defined(windows):
var
styleCache = initTable[int, string]() styleCache = initTable[int, string]()
var var
@ -41,9 +39,7 @@ var
const const
fgPrefix = "\x1b[38;2;" fgPrefix = "\x1b[38;2;"
bgPrefix = "\x1b[48;2;" bgPrefix = "\x1b[48;2;"
ansiResetCode* = "\e[0m"
when not defined(windows):
const
stylePrefix = "\e[" stylePrefix = "\e["
when defined(windows): when defined(windows):
@ -468,7 +464,7 @@ proc resetAttributes*(f: File) =
else: else:
discard setConsoleTextAttribute(hStdout, oldStdoutAttr) discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
else: else:
f.write("\e[0m") f.write(ansiResetCode)
type type
Style* = enum ## different styles for text output Style* = enum ## different styles for text output
@ -487,7 +483,7 @@ when not defined(windows):
gFG {.threadvar.}: int gFG {.threadvar.}: int
gBG {.threadvar.}: int gBG {.threadvar.}: int
proc getStyleStr(style: int): string = proc ansiStyleCode*(style: int): string =
when hasThreadSupport: when hasThreadSupport:
result = fmt"{stylePrefix}{style}m" result = fmt"{stylePrefix}{style}m"
else: else:
@ -497,6 +493,13 @@ when not defined(windows):
result = fmt"{stylePrefix}{style}m" result = fmt"{stylePrefix}{style}m"
styleCache[style] = result 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]) = proc setStyle*(f: File, style: set[Style]) =
## Sets the terminal style. ## Sets the terminal style.
when defined(windows): when defined(windows):
@ -510,7 +513,7 @@ proc setStyle*(f: File, style: set[Style]) =
discard setConsoleTextAttribute(h, old or a) discard setConsoleTextAttribute(h, old or a)
else: else:
for s in items(style): for s in items(style):
f.write(getStyleStr(ord(s))) f.write(ansiStyleCode(s))
proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
## Writes the text `txt` in a given `style` to stdout. ## 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.write(txt)
stdout.resetAttributes() stdout.resetAttributes()
if gFG != 0: if gFG != 0:
stdout.write(getStyleStr(gFG)) stdout.write(ansiStyleCode(gFG))
if gBG != 0: if gBG != 0:
stdout.write(getStyleStr(gBG)) stdout.write(ansiStyleCode(gBG))
type type
ForegroundColor* = enum ## terminal's foreground colors ForegroundColor* = enum ## terminal's foreground colors
@ -572,7 +575,7 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
else: else:
gFG = ord(fg) gFG = ord(fg)
if bright: inc(gFG, 60) if bright: inc(gFG, 60)
f.write(getStyleStr(gFG)) f.write(ansiStyleCode(gFG))
proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
## Sets the terminal's background color. ## Sets the terminal's background color.
@ -594,10 +597,18 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
else: else:
gBG = ord(bg) gBG = ord(bg)
if bright: inc(gBG, 60) 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: when hasThreadSupport:
let rgb = extractRGB(color) let rgb = extractRGB(color)
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" 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" result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result 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: when hasThreadSupport:
let rgb = extractRGB(color) let rgb = extractRGB(color)
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" 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" result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result 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) = proc setForegroundColor*(f: File, color: Color) =
## Sets the terminal's foreground true color. ## Sets the terminal's foreground true color.
if trueColorIsEnabled: if trueColorIsEnabled:
f.write(getFGColorStr(color)) f.write(ansiForegroundColorCode(color))
proc setBackgroundColor*(f: File, color: Color) = proc setBackgroundColor*(f: File, color: Color) =
## Sets the terminal's background true color. ## Sets the terminal's background true color.
if trueColorIsEnabled: if trueColorIsEnabled:
f.write(getBGColorStr(color)) f.write(ansiBackgroundColorCode(color))
proc setTrueColor(f: File, color: Color) = proc setTrueColor(f: File, color: Color) =
if fgSetColor: if fgSetColor:

View file

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

View file

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

View file

@ -1,4 +1,6 @@
import sets import sets
import hashes
import algorithm
block setEquality: block setEquality:
var var
@ -74,6 +76,48 @@ block orderedSetClearWorked:
doAssert r == "zeat jos" 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 """ discard """
output: "seq[float]\n0" output: "seq[float]\n0"
targets: "c cpp"
""" """
# https://github.com/nim-lang/Nim/issues/5602 # https://github.com/nim-lang/Nim/issues/5602
import typetraits import typetraits, module_with_generics
type type
Foo[T] = object of RootObj Foo[T] = object of RootObj
@ -16,3 +17,8 @@ proc p[T](f: Foo[T]): T =
var s: Bar[float] var s: Bar[float]
echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead 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 a
b b
c 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" 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() = proc main() =
when defined(windows): 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", let p = tryGetUnicodeValue(r"Environment", "Path",
HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue( HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue(
r"System\CurrentControlSet\Control\Session Manager\Environment", "Path", r"System\CurrentControlSet\Control\Session Manager\Environment", "Path",
HKEY_LOCAL_MACHINE) HKEY_LOCAL_MACHINE)
var alreadyInPath = false var nimAlreadyInPath = false
var nimbleAlreadyInPath = false
var mingWchoices: seq[string] = @[] var mingWchoices: seq[string] = @[]
var incompat: seq[string] = @[] var incompat: seq[string] = @[]
for x in p.split(';'): for x in p.split(';'):
@ -200,18 +202,29 @@ proc main() =
let y = try: expandFilename(if x[0] == '"' and x[^1] == '"': let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
substr(x, 1, x.len-2) else: x) substr(x, 1, x.len-2) else: x)
except: "" except: ""
if y.cmpIgnoreCase(desiredPath) == 0: alreadyInPath = true if y.cmpIgnoreCase(nimDesiredPath) == 0:
if y.toLowerAscii.contains("mingw"): nimAlreadyInPath = true
elif y.cmpIgnoreCase(nimbleDesiredPath) == 0:
nimbleAlreadyInPath = true
elif y.toLowerAscii.contains("mingw"):
if dirExists(y): if dirExists(y):
if checkGccArch(y): mingWchoices.add y if checkGccArch(y): mingWchoices.add y
else: incompat.add y else: incompat.add y
if alreadyInPath: if nimAlreadyInPath:
echo "bin/nim.exe is already in your PATH [Skipping]" echo "bin\\nim.exe is already in your PATH [Skipping]"
else: else:
if askBool("nim.exe is not in your PATH environment variable.\n" & if askBool("nim.exe is not in your PATH environment variable.\n" &
"Should it be added permanently? (y/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: if mingWchoices.len == 0:
# No mingw in path, so try a few locations: # No mingw in path, so try a few locations:
let alternative = tryDirs(incompat, defaultMingwLocations()) let alternative = tryDirs(incompat, defaultMingwLocations())