diff options
-rw-r--r-- | compiler/rodimpl.nim | 6 | ||||
-rw-r--r-- | compiler/semcall.nim | 2 | ||||
-rw-r--r-- | compiler/seminst.nim | 2 | ||||
-rw-r--r-- | compiler/semstmts.nim | 4 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 2 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 8 | ||||
-rw-r--r-- | compiler/transf.nim | 32 | ||||
-rw-r--r-- | compiler/types.nim | 2 | ||||
-rw-r--r-- | lib/pure/endians.nim | 21 | ||||
-rw-r--r-- | lib/pure/terminal.nim | 8 | ||||
-rw-r--r-- | lib/system/nimscript.nim | 4 | ||||
-rw-r--r-- | testament/tester.nim | 2 | ||||
-rw-r--r-- | tests/generics/tparser_generator.nim | 415 | ||||
-rw-r--r-- | tests/iter/tclosureiters.nim | 60 | ||||
-rw-r--r-- | tests/stdlib/t9394.nim | 7 | ||||
-rw-r--r-- | tests/trmacros/tstmtlist.nim | 15 |
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() |