diff --git a/changelog.md b/changelog.md index 9af2aae83a..cbefbc421e 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/compiler/ast.nim b/compiler/ast.nim index 5e65fbff90..5a84b2b021 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index f9311d4cec..d98a42b34f 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -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() diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 311bb6539e..01c56ec9c6 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -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 diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 9bdc41016d..f93355e6bc 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -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) diff --git a/compiler/importer.nim b/compiler/importer.nim index ed988cea72..90e774a502 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -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) diff --git a/compiler/modules.nim b/compiler/modules.nim index 7bc32d42cc..5d1eba1f26 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -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: diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index ecb4b00931..c32dbe623f 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -8,7 +8,7 @@ # import strutils, lexbase, streams -import "../compiler" / [ast, msgs, idents] +import ".." / [ast, msgs, idents] from os import splitFile type diff --git a/compiler/parser.nim b/compiler/parser.nim index 7a23ed6a9b..082dcd9ed1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1bb10b80ef..de98a5e42a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index ad5bdfd497..12ac19ca3a 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2a2a8b72af..6b32fa66c0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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.. 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) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7282ea58cf..2d4b5ae778 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 74c944d516..1d4515875d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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)) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 18d68bd2d4..4bc153e464 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index a5c053488b..9c2ad71c5a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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: diff --git a/compiler/vm.nim b/compiler/vm.nim index b001cdc074..dcb4cefd71 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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[][] diff --git a/config/nim.cfg b/config/nim.cfg index a2a774b236..9b3172e90a 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -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 diff --git a/doc/manual.rst b/doc/manual.rst index fabcf058ad..44e2ee5b5f 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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] diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1dc067e1ac..fa5cd67dfb 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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. diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index f88037e2f3..b1541c1ba1 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -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 diff --git a/lib/js/dom.nim b/lib/js/dom.nim index cd76097290..fd81fdf3f1 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -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 diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 32b6387ad4..59c90bc2ba 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -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: diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index ca4f80f2a4..6ec71e912c 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -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 diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 06bf126655..fc04ef1af4 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -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 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 3f01f02853..ee3072c855 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -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") diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 5249e23c2f..fcca4d5d74 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -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: diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index c38d360263..8cd47aa395 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -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(""") 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("") diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index fa3700190e..56ebde823e 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -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) { diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim index 6139560bd2..61e14260af 100644 --- a/tests/collections/tsets.nim +++ b/tests/collections/tsets.nim @@ -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 diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim new file mode 100644 index 0000000000..e801a4790e --- /dev/null +++ b/tests/generics/module_with_generics.nim @@ -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 + diff --git a/tests/generics/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim index 6d48c796e6..ee5ba89d50 100644 --- a/tests/generics/t5602_inheritence.nim +++ b/tests/generics/t5602_inheritence.nim @@ -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) + diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim new file mode 100644 index 0000000000..05bb52a402 --- /dev/null +++ b/tests/macros/tstructuredlogging.nim @@ -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") + diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim new file mode 100644 index 0000000000..8d9c9ec025 --- /dev/null +++ b/tests/macros/ttemplatesymbols.nim @@ -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 + +""" + diff --git a/tests/modules/definitions.nim b/tests/modules/definitions.nim new file mode 100644 index 0000000000..edc6eaa6d4 --- /dev/null +++ b/tests/modules/definitions.nim @@ -0,0 +1,4 @@ +var v*: int +proc p* = echo "proc p called" +template t* = echo "template t expanded" + diff --git a/tests/modules/proxy_module.nim b/tests/modules/proxy_module.nim new file mode 100644 index 0000000000..c244688cdd --- /dev/null +++ b/tests/modules/proxy_module.nim @@ -0,0 +1,3 @@ +import definitions +export definitions except p + diff --git a/tests/overload/tparam_forwarding.nim b/tests/overload/tparam_forwarding.nim index c1b276bfc9..b0eea42c71 100644 --- a/tests/overload/tparam_forwarding.nim +++ b/tests/overload/tparam_forwarding.nim @@ -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 + diff --git a/tests/threads/tthreadvars.nim b/tests/threads/tthreadvars.nim new file mode 100644 index 0000000000..81aa2e5ecf --- /dev/null +++ b/tests/threads/tthreadvars.nim @@ -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 + diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim new file mode 100644 index 0000000000..517a67fb00 --- /dev/null +++ b/tests/vm/tref.nim @@ -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" \ No newline at end of file diff --git a/tools/finish.nim b/tools/finish.nim index b5ef78b65b..4f2c725953 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -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())