summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/rodimpl.nim6
-rw-r--r--compiler/semcall.nim2
-rw-r--r--compiler/seminst.nim2
-rw-r--r--compiler/semstmts.nim4
-rw-r--r--compiler/semtypinst.nim2
-rw-r--r--compiler/sigmatch.nim8
-rw-r--r--compiler/transf.nim32
-rw-r--r--compiler/types.nim2
-rw-r--r--lib/pure/endians.nim21
-rw-r--r--lib/pure/terminal.nim8
-rw-r--r--lib/system/nimscript.nim4
-rw-r--r--testament/tester.nim2
-rw-r--r--tests/generics/tparser_generator.nim415
-rw-r--r--tests/iter/tclosureiters.nim60
-rw-r--r--tests/stdlib/t9394.nim7
-rw-r--r--tests/trmacros/tstmtlist.nim15
16 files changed, 550 insertions, 40 deletions
diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index 420c5bf7f..eab305d5c 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -616,7 +616,7 @@ proc loadType(g; id: int; info: TLineInfo): PType =
     doAssert b.s[b.pos] == '\20'
     inc(b.pos)
     let y = loadSym(g, decodeVInt(b.s, b.pos), info)
-    result.methods.safeAdd((x, y))
+    result.methods.add((x, y))
   decodeLoc(g, b, result.loc, info)
   while b.s[b.pos] == '^':
     inc(b.pos)
@@ -656,7 +656,7 @@ proc decodeInstantiations(g; b; info: TLineInfo;
     if b.s[b.pos] == '\20':
       inc(b.pos)
       ii.compilesId = decodeVInt(b.s, b.pos)
-    s.safeAdd ii
+    s.add ii
 
 proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
   if b.s[b.pos] == '{':
@@ -717,7 +717,7 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
   of skType, skGenericParam:
     while b.s[b.pos] == '\14':
       inc(b.pos)
-      result.typeInstCache.safeAdd loadType(g, decodeVInt(b.s, b.pos), result.info)
+      result.typeInstCache.add loadType(g, decodeVInt(b.s, b.pos), result.info)
   of routineKinds:
     decodeInstantiations(g, b, result.info, result.procInstCache)
     if b.s[b.pos] == '\16':
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 61d6113dc..49b344274 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -105,7 +105,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           if cmp < 0: best = z   # x is better than the best so far
           elif cmp == 0: alt = z # x is as good as the best so far
       elif errorsEnabled or z.diagnosticsEnabled:
-        errors.safeAdd(CandidateError(
+        errors.add(CandidateError(
           sym: sym,
           unmatchedVarParam: int z.mutabilityProblem,
           firstMismatch: z.firstMismatch,
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 833edacac..b6936d1be 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -377,7 +377,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     #if c.compilesContextId == 0:
     rawHandleSelf(c, result)
     entry.compilesId = c.compilesContextId
-    fn.procInstCache.safeAdd(entry)
+    fn.procInstCache.add(entry)
     c.generics.add(makeInstPair(fn, entry))
     if n.sons[pragmasPos].kind != nkEmpty:
       pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index d2b866366..a286cdc85 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -66,7 +66,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
         localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
     else:
       localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
-  elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
+  elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt):
     localError(c.config, n.info, errInvalidControlFlowX %
                renderTree(n, {renderNoComments}))
 
@@ -1517,7 +1517,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
                                       tyAlias, tySink})
         if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len:
           foundObj = true
-          x.methods.safeAdd((col,s))
+          x.methods.add((col,s))
     if not foundObj:
       message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
   else:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index bf06b019f..b05fb37ae 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -69,7 +69,7 @@ proc cacheTypeInst*(inst: PType) =
   let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
   if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
     return
-  gt.sym.typeInstCache.safeAdd(inst)
+  gt.sym.typeInstCache.add(inst)
 
 
 type
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 2fc98c69b..4fd4a3205 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -728,7 +728,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
                       else:
                         makeTypeDesc(c, typ)
 
-        typeParams.safeAdd((param, typ))
+        typeParams.add((param, typ))
 
       addDecl(c, param)
 
@@ -757,7 +757,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   if collectDiagnostics:
     m.c.config.writelnHook = oldWriteHook
     for msg in diagnostics:
-      m.diagnostics.safeAdd msg
+      m.diagnostics.add msg
       m.diagnosticsEnabled = true
 
   if checkedBody == nil: return nil
@@ -1027,7 +1027,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
 
       result = typeRel(c, aOrig.base, candidate)
       if result != isNone:
-        c.inferredTypes.safeAdd aOrig
+        c.inferredTypes.add aOrig
         aOrig.sons.add candidate
         result = isEqual
       return
@@ -1727,7 +1727,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     else:
       result = typeRel(c, f.base, a)
       if result != isNone:
-        c.inferredTypes.safeAdd f
+        c.inferredTypes.add f
         f.sons.add a
 
   of tyTypeDesc:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 6b6335129..c2f6c799a 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -190,26 +190,32 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
       var L = sonsLen(it)
       var defs = newTransNode(it.kind, it.info, L)
       for j in countup(0, L-3):
-        let x = freshVar(c, it.sons[j].sym)
-        idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
-        defs[j] = x.PTransNode
+        if it[j].kind == nkSym:
+          let x = freshVar(c, it.sons[j].sym)
+          idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
+          defs[j] = x.PTransNode
+        else:
+          defs[j] = transform(c, it[j])
       assert(it.sons[L-2].kind == nkEmpty)
       defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
       defs[L-1] = transform(c, it.sons[L-1])
       result[i] = defs
 
 proc transformConstSection(c: PTransf, v: PNode): PTransNode =
-  result = newTransNode(v)
-  for i in countup(0, sonsLen(v)-1):
-    var it = v.sons[i]
-    if it.kind == nkCommentStmt:
-      result[i] = PTransNode(it)
-    else:
-      if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
-      if it.sons[0].kind != nkSym:
-        internalError(c.graph.config, it.info, "transformConstSection")
+  result = PTransNode(v)
+  when false:
+    result = newTransNode(v)
+    for i in countup(0, sonsLen(v)-1):
+      var it = v.sons[i]
+      if it.kind == nkCommentStmt:
+        result[i] = PTransNode(it)
+      else:
+        if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
+        if it.sons[0].kind != nkSym:
+          debug it.sons[0]
+          internalError(c.graph.config, it.info, "transformConstSection")
 
-      result[i] = PTransNode(it)
+        result[i] = PTransNode(it)
 
 proc hasContinue(n: PNode): bool =
   case n.kind
diff --git a/compiler/types.nim b/compiler/types.nim
index 6f7d08d38..485d3c369 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -406,7 +406,7 @@ const
 const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg}
 
 template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
-  tc.sons.safeAdd concrete
+  tc.sons.add concrete
   tc.flags.incl tfResolved
 
 # TODO: It would be a good idea to kill the special state of a resolved
diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim
index 6f80d56ef..771ecaaca 100644
--- a/lib/pure/endians.nim
+++ b/lib/pure/endians.nim
@@ -44,20 +44,23 @@ else:
   const useBuiltinSwap = false
 
 when useBuiltinSwap:
+  template swapOpImpl(T: typedesc, op: untyped) =
+    ## We have to use `copyMem` here instead of a simple deference because they
+    ## may point to a unaligned address. A sufficiently smart compiler _should_
+    ## be able to elide them when they're not necessary.
+    var tmp: T
+    copyMem(addr tmp, inp, sizeOf(T))
+    tmp = op(tmp)
+    copyMem(outp, addr tmp, sizeOf(T))
+
   proc swapEndian64*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint64](inp)
-    var o = cast[ptr uint64](outp)
-    o[] = builtin_bswap64(i[])
+    swapOpImpl(uint64, builtin_bswap64)
 
   proc swapEndian32*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint32](inp)
-    var o = cast[ptr uint32](outp)
-    o[] = builtin_bswap32(i[])
+    swapOpImpl(uint32, builtin_bswap32)
 
   proc swapEndian16*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint16](inp)
-    var o = cast[ptr uint16](outp)
-    o[] = builtin_bswap16(i[])
+    swapOpImpl(uint16, builtin_bswap16)
 
 else:
   proc swapEndian64*(outp, inp: pointer) =
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 2e138b27e..974dc839d 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -18,7 +18,7 @@
 
 import macros
 import strformat
-from strutils import toLowerAscii
+from strutils import toLowerAscii, `%`
 import colors, tables
 
 when defined(windows):
@@ -635,7 +635,8 @@ proc ansiForegroundColorCode*(color: Color): string =
 
 template ansiForegroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
-  (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+  # no usage of `fmt`, see issue #7632
+  (static("$1$2;$3;$4m" % [$fgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
 
 proc ansiBackgroundColorCode*(color: Color): string =
   let rgb = extractRGB(color)
@@ -643,7 +644,8 @@ proc ansiBackgroundColorCode*(color: Color): string =
 
 template ansiBackgroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
-  (static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+  # no usage of `fmt`, see issue #7632
+  (static("$1$2;$3;$4m" % [$bgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
 
 proc setForegroundColor*(f: File, color: Color) =
   ## Sets the terminal's foreground true color.
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index c876d6d06..0adc7a83c 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -282,7 +282,9 @@ proc projectPath*(): string =
   builtin
 
 proc thisDir*(): string =
-  ## Retrieves the location of the current ``nims`` script file.
+  ## Retrieves the directory of the current ``nims`` script file. Its path is
+  ## obtained via ``currentSourcePath`` (although, currently,
+  ## ``currentSourcePath`` resolves symlinks, unlike ``thisDir``).
   builtin
 
 proc cd*(dir: string) {.raises: [OSError].} =
diff --git a/testament/tester.nim b/testament/tester.nim
index 1142cc8c0..c48e4441d 100644
--- a/testament/tester.nim
+++ b/testament/tester.nim
@@ -313,7 +313,7 @@ proc testSpec(r: var TResults, test: TTest, target = targetC) =
     inc(r.total)
     return
 
-  if getEnv("NIM_COMPILE_TO_CPP", "false") == "true" and target == targetC and expected.targets == {}:
+  if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true" and target == targetC and expected.targets == {}:
     expected.targets.incl(targetCpp)
   elif expected.targets == {}:
     expected.targets.incl(target)
diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim
new file mode 100644
index 000000000..01ddd29b8
--- /dev/null
+++ b/tests/generics/tparser_generator.nim
@@ -0,0 +1,415 @@
+discard """
+  output: '''Match failed: spam
+Match failed: ham'''
+"""
+
+# bug #6220
+
+import nre
+import options
+import strutils except isAlpha, isLower, isUpper, isSpace
+from unicode import isAlpha, isLower, isUpper, isTitle, isWhiteSpace
+import os
+
+const debugLex = false
+
+template debug(enable: bool, text: string): typed =
+  when enable:
+    echo(text)
+
+type
+  Parser[N, T] = proc(text: T, start: int, nodes: var seq[Node[N]]): int {.closure.}
+
+  RuleObj[N, T] = object
+    parser: Parser[N, T]
+    kind: N
+
+  Rule[N, T] = ref RuleObj[N, T]
+
+  NodeKind = enum
+    terminal,
+    nonterminal
+
+  Node*[N] = object of RootObj
+    # Uncomment the following lines and the compiler crashes
+    # case nodeKind: NodeKind
+    #   of nonterminal:
+    #     kids: Node[N]
+    #   of terminal:
+    #     discard
+    start*: int
+    length*: int
+    kind*: N
+
+
+  NonTerminal[N] = object of Node
+    children: seq[Node[N]]
+
+proc newRule[N, T](parser: Parser, kind: N): Rule[N, T] =
+  new(result)
+  result.parser = parser
+  result.kind = kind
+
+proc newRule[N, T](kind: N): Rule[N, T] =
+  new(result)
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, kind: N): Node[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, children: seq[Node[N]], kind: N): NonTerminal[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+  result.children = children
+
+proc substr[T](text: T, first, last: int): T =
+  text[first .. last]
+
+proc continuesWith[N](text: seq[Node[N]], subtext: seq[N], start: Natural): bool =
+  let length = len(text)
+  var pos = 0
+  while pos < len(subtext):
+    let textpos = start + pos
+    if textpos == len(text):
+      return false
+    if text[textpos].kind != subtext[pos].kind:
+      return false
+    pos+=1
+  return true
+
+
+proc render*[N, T](text: T, nodes: seq[Node[N]]): string =
+  ## Uses a sequence of Nodes to render a given text string
+  result = ""
+  for node in nodes:
+    result.add("<" & node.value(text) & ">")
+
+proc render*[N, T](rule: Rule[N, T], text: string): string =
+  ## Uses a rule to render a given text string
+  render(text, rule.parse(text))
+
+proc render*[N, T](text: T, nodes: seq[Node[N]], source: string): string =
+  result = ""
+  for node in nodes:
+    result.add("[" & node.value(text, source) & "]")
+
+proc render*[N, T, X](rule: Rule[N, T], text: seq[Node[X]], source: string): string =
+  ## Uses a rule to render a given series of nodes, providing the source string
+  text.render(rule.parse(text, source = source), source)
+
+proc annotate*[N, T](node: Node[N], text: T): string =
+  result = "<" & node.value(text) & ":" & $node.kind & ">"
+
+proc annotate*[N, T](nodes: seq[Node[N]], text: T): string =
+  result = ""
+  for node in nodes:
+    result.add(node.annotate(text))
+
+proc annotate*[N, T](rule: Rule[N, T], text: T): string =
+  annotate(rule.parse(text), text)
+
+proc value*[N, T](node: Node[N], text: T): string =
+  result = $text.substr(node.start, node.start + node.length - 1)
+
+proc value*[N, X](node: Node[N], text: seq[Node[X]], source: string): string =
+  result = ""
+  for n in node.start ..< node.start + node.length:
+    result &= text[n].annotate(source)
+
+proc parse*[N, T](rule: Rule[N, T], text: T, start = 0, source: string = ""): seq[Node[N]] =
+  result = newSeq[Node[N]]()
+  debug(debugLex, "Parsing: " & $text)
+  let length = rule.parser(text, start, result)
+
+  when T is string:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+  else:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+
+
+proc literal*[N, T, P](pattern: P, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    if start == len(text):
+      return -1
+    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    when P is string or P is seq[N]:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start..start+len(pattern)-1])
+      if text.continuesWith(pattern, start):
+        let node = initNode(start, len(pattern), kind)
+        nodes.add(node)
+        debug(debugLex, "Literal: matched <" & $text[start ..< start+node.length] & ":" & $node.length & ">" )
+        return node.length
+    elif P is char:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start] == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    else:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start].kind == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    return -1
+  result = newRule[N, T](parser, kind)
+
+proc token[N, T](pattern: T, kind: N): Rule[N, T] =
+  when T is not string:
+     {.fatal: "Token is only supported for strings".}
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start)
+    if start == len(text):
+      return -1
+    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    let m = text.match(re(pattern), start)
+    if m.isSome:
+      let node = initNode(start, len(m.get.match), kind)
+      nodes.add(node)
+      result = node.length
+      debug(debugLex, "Token: matched <" & text[start ..< start+node.length] & ":" & $node.length & ">" )
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+proc chartest[N, T, S](testfunc: proc(s: S): bool, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    if start == len(text):
+      return -1
+    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    if testfunc(text[start]):
+      nodes.add(initNode(start, 1, kind))
+      result = 1
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+proc any*[N, T, S](symbols: T, kind: N): Rule[N, T] =
+  let test = proc(s: S): bool =
+    when S is string:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & symbols.replace("\n", "\\n").replace("\r", "\\r"))
+    else:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & $symbols)
+    result = s in symbols
+  result = chartest[N, T, S](test, kind)
+
+proc ignore*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+  result = newRule[N, T](parser, rule.kind)
+
+proc combine*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    nodes.add(initNode(start, result, kind))
+  result = newRule[N, T](parser, kind)
+
+proc build*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    let nonTerminal = initNode(start, result, mynodes, kind)
+    nodes.add(nonTerminal)
+  result = newRule[N, T](parser, kind)
+
+proc fail*[N, T](message: string, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let lineno = countLines(text[0..start])
+    var startline = start
+    var endline = start
+    while startline>0:
+      if text[startline] in NewLines:
+        break
+      startline-=1
+    while endline < len(text):
+      if text[endline] in NewLines:
+        break
+      endline+=1
+    let charno = start-startline
+    echo text.substr(startline, endline)
+    echo ' '.repeat(max(charno,0)) & '^'
+    raise newException(ValueError, "Position: " & $start & " Line: " & $lineno & ", Symbol: " & $charno & ": " & message)
+  result = newRule[N, T](parser, kind)
+
+proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    assert(not isNil(left.parser), "Left hand side parser is nil")
+    let leftlength = left.parser(text, start, mynodes)
+    if leftlength == -1:
+      return leftlength
+    assert(not isNil(right.parser), "Right hand side parser is nil")
+    let rightlength = right.parser(text, start+leftlength, mynodes)
+    if rightlength == -1:
+      return rightlength
+    result = leftlength + rightlength
+    nodes.add(mynodes)
+  result = newRule[N, T](parser, left.kind)
+
+proc `/`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    assert(not isNil(left.parser), "Left hand side of / is not fully defined")
+    let leftlength = left.parser(text, start, mynodes)
+    if leftlength != -1:
+      nodes.add(mynodes)
+      return leftlength
+    mynodes = newSeq[Node[N]]()
+    assert(not isNil(right.parser), "Right hand side of / is not fully defined")
+    let rightlength = right.parser(text, start, mynodes)
+    if rightlength == -1:
+      return rightlength
+    nodes.add(mynodes)
+    return rightlength
+  result = newRule[N, T](parser, left.kind)
+
+proc `?`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = rule.parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+proc `+`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var success = rule.parser(text, start, nodes)
+    if success == -1:
+      return success
+    var total = 0
+    while success != -1 and start+total < len(text):
+      total += success
+      success = rule.parser(text, start+total, nodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = (+rule).parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+#Note: this consumes - for zero-width lookahead see !
+proc `^`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success == -1: 1 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](repetitions: int, rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var total = 0
+    for i in 0..<repetitions:
+      let success = rule.parser(text, start+total, mynodes)
+      if success == -1:
+        return success
+      else:
+        total += success
+    nodes.add(mynodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+# Positive zero-width lookahead
+proc `&`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success != -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+# Negative zero-width lookahead
+proc `!`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let failure = rule.parser(text, start, mynodes)
+    return if failure == -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var length = 0
+    var success = rule.parser(text, start+length, mynodes)
+    while success == -1 and start+length < len(text):
+      length += 1
+      success = rule.parser(text, start+length, mynodes)
+    if start+length >= len(text):
+      result = -1
+    else:
+      nodes.add(initNode(start, length, rule.kind))
+      nodes.add(mynodes)
+      result = length + success
+  result = newRule[N, T](parser, rule.kind)
+
+proc `->`*(rule: Rule, production: Rule) =
+  assert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?")
+  rule.parser = production.parser
+
+template grammar*[K](Kind, Text, Symbol: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
+
+    proc newRule(): Rule[Kind, Text] {.inject.} = newRule[Kind, Text](default)
+    proc chartest(testfunc: proc(c: Symbol): bool): Rule[Kind, Text] {.inject.} = chartest[Kind, Text, Symbol](testfunc, default)
+    proc literal[P](pattern: P, kind: K): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, kind)
+    proc literal[P](pattern: P): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, default)
+
+    when Text is string:
+      proc token(pattern: string): Rule[Kind, Text] {.inject.} = token(pattern, default)
+      proc fail(message: string): Rule[Kind, Text] {.inject.} = fail[Kind, Text](message, default)
+      let alpha {.inject.} = chartest[Kind, Text, Symbol](isAlphaAscii, default)
+      let alphanumeric {.inject.}= chartest[Kind, Text, Symbol](isAlphaNumeric, default)
+      let digit {.inject.} = chartest[Kind, Text, Symbol](isDigit, default)
+      let lower {.inject.} = chartest[Kind, Text, Symbol](isLowerAscii, default)
+      let upper {.inject.} = chartest[Kind, Text, Symbol](isUpperAscii, default)
+      let isspace = proc (x: char): bool = x.isSpaceAscii and not (x in NewLines)
+      let space {.inject.} = chartest[Kind, Text, Symbol](isspace, default)
+      let isnewline = proc (x: char): bool = x in NewLines
+      let newline {.inject.} = chartest[Kind, Text, Symbol](isnewline, default)
+      let alphas {.inject.} = combine(+alpha, default)
+      let alphanumerics {.inject.} = combine(+alphanumeric, default)
+      let digits {.inject.} = combine(+digit, default)
+      let lowers {.inject.} = combine(+lower, default)
+      let uppers {.inject.} = combine(+upper, default)
+      let spaces {.inject.} = combine(+space, default)
+      let newlines {.inject.} = combine(+newline, default)
+
+    proc any(chars: Text): Rule[Kind, Text] {.inject.} = any[Kind, Text, Symbol](chars, default)
+    proc combine(rule: Rule[Kind, Text]): Rule[Kind, Text] {.inject.} = combine[Kind, Text](rule, default)
+
+    code
+
+template grammar*[K](Kind: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
+  grammar(Kind, string, char, default, code)
+
+when isMainModule:
+  block:
+    type DummyKind = enum dkDefault
+    grammar(DummyKind, string, char, dkDefault):
+      let rule = token("h[a]+m") + ignore(token(r"\s+")) + (literal("eggs") / literal("beans"))
+      var text = "ham beans"
+      discard rule.parse(text)
+
+      var recursive = newRule()
+      recursive -> (literal("(") + recursive + literal(")")) / token(r"\d+")
+      for test in ["spam", "57", "(25)", "((25))"]:
+        discard recursive.parse(test)
+
+      let repeated = +literal("spam") + ?literal("ham") + *literal("salami")
+      for test in ["ham", "spam", "spamspamspam" , "spamham", "spamsalami", "spamsalamisalami"]:
+        discard  repeated.parse(test)
diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim
index 37313d4d7..345a4867a 100644
--- a/tests/iter/tclosureiters.nim
+++ b/tests/iter/tclosureiters.nim
@@ -79,3 +79,63 @@ proc foo(f: (iterator(): int)) =
 
 let fIt = iterator(): int = yield 70
 foo fIt
+
+# bug #5321
+
+proc lineIter*(filename: string): iterator(): string =
+  result = iterator(): string {.closure.} =
+    for line in lines(filename):
+      yield line
+
+proc unused =
+  var count = 0
+  let iter = lineIter("temp10.nim")
+  for line in iter():
+    count += 1
+
+iterator lineIter2*(filename: string): string {.closure.} =
+  var f = open(filename, bufSize=8000)
+  defer: close(f)   # <-- commenting defer "solves" the problem
+  var res = TaintedString(newStringOfCap(80))
+  while f.readLine(res): yield res
+
+proc unusedB =
+  var count = 0
+  for line in lineIter2("temp10.nim"):
+    count += 1
+
+# bug #5519
+import os, algorithm
+
+iterator filesIt(path: string): auto {.closure.} =
+  var files = newSeq[string]()
+  var dirs = newSeq[string]()
+  for k, p in os.walkDir(path):
+    let (_, n, e) = p.splitFile
+    if e != "":
+      continue
+    case k
+    of pcFile, pcLinkToFile:
+      files.add(n)
+    else:
+      dirs.add(n)
+  files.sort(system.cmp)
+  dirs.sort(system.cmp)
+  for f in files:
+    yield f
+
+  for d in dirs:
+    files = newSeq[string]()
+    for k, p in os.walkDir(path / d):
+      let (_, n, e) = p.splitFile
+      if e != "":
+        continue
+      case k
+      of pcFile, pcLinkToFile:
+        files.add(n)
+      else:
+        discard
+    files.sort(system.cmp)
+    let prefix = path.splitPath[1]
+    for f in files:
+      yield prefix / f
diff --git a/tests/stdlib/t9394.nim b/tests/stdlib/t9394.nim
new file mode 100644
index 000000000..3c0123eb5
--- /dev/null
+++ b/tests/stdlib/t9394.nim
@@ -0,0 +1,7 @@
+import terminal, colors
+
+let codeFg = ansiForegroundColorCode(colAliceBlue)
+let codeBg = ansiBackgroundColorCode(colAliceBlue)
+
+doAssert codeFg == "\27[38;2;240;248;255m"
+doAssert codeBg == "\27[48;2;240;248;255m"
diff --git a/tests/trmacros/tstmtlist.nim b/tests/trmacros/tstmtlist.nim
index 751acb79a..8261e7c45 100644
--- a/tests/trmacros/tstmtlist.nim
+++ b/tests/trmacros/tstmtlist.nim
@@ -17,3 +17,18 @@ if true:
   writeLine stdout, "2"
   write stdout, "3"
   echo "4"
+
+# bug #7972
+
+template optimizeLogWrites*{
+  write(f, x)
+  write(f, y)
+}(x, y: string{lit}, f: File) =
+  write(f, x & y)
+
+proc foo() =
+  const N = 1
+  stdout.write("")
+  stdout.write("")
+
+foo()