From 45d74f408151f21f1593bfc04bd29ea7509ddae9 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 17 Oct 2017 12:46:49 +0200 Subject: destructors: preparations for upcoming changes --- compiler/semdata.nim | 1 + 1 file changed, 1 insertion(+) (limited to 'compiler/semdata.nim') diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a3f0f715b..8b5803a7f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -70,6 +70,7 @@ type TTypeAttachedOp* = enum attachedAsgn, + attachedSink, attachedDeepCopy, attachedDestructor -- cgit 1.4.1-2-gfad0 From 3aa7c2232e0f58d81d97e6cdecc86908721d55b8 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 25 Oct 2017 08:39:56 +0200 Subject: destructors: supportsCopyMem finally works for recursive types --- compiler/semasgn.nim | 2 ++ compiler/semdata.nim | 6 ++++++ compiler/semtypes.nim | 12 ++++++++---- compiler/semtypinst.nim | 36 ++++++++++++++++++++++++++---------- compiler/sigmatch.nim | 3 ++- tests/destructor/topttree.nim | 12 +++++++++--- 6 files changed, 53 insertions(+), 18 deletions(-) (limited to 'compiler/semdata.nim') diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 5ae9feec2..b93cb34da 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -319,6 +319,8 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = ## to ensure we lift assignment, destructors and moves properly. ## The later 'destroyer' pass depends on it. if not newDestructors or not hasDestructor(typ): return + # do not produce wrong liftings while we're still instantiating generics: + if c.typesWithOps.len > 0: return let typ = typ.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: if typ.destructor == nil: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8b5803a7f..b6b5db101 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -132,6 +132,11 @@ type recursiveDep*: string suggestionsMade*: bool inTypeContext*: int + typesWithOps*: seq[(PType, PType)] #\ + # We need to instantiate the type bound ops lazily after + # the generic type has been constructed completely. See + # tests/destructor/topttree.nim for an example that + # would otherwise fail. proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s @@ -219,6 +224,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext result.cache = cache result.graph = graph initStrTable(result.signatures) + result.typesWithOps = @[] proc inclSym(sq: var TSymSeq, s: PSym) = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index fbb5d0b6b..06c76a853 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1295,8 +1295,7 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil - when defined(nimsuggest): - inc c.inTypeContext + inc c.inTypeContext if gCmd == cmdIdeTools: suggestExpr(c, n) case n.kind @@ -1511,8 +1510,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) n.typ = result - when defined(nimsuggest): - dec c.inTypeContext + dec c.inTypeContext + if c.inTypeContext == 0: instAllTypeBoundOp(c, n.info) + +when false: + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = + result = semTypeNodeInner(c, n, prev) + instAllTypeBoundOp(c, n.info) proc setMagicType(m: PSym, kind: TTypeKind, size: int) = # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86 diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 3b99ea562..0f49398f2 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -77,7 +77,7 @@ type topLayer*: TIdTable nextLayer*: ptr LayeredIdTable - TReplTypeVars* {.final.} = object + TReplTypeVars* = object c*: PContext typeMap*: ptr LayeredIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym @@ -261,6 +261,17 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = if not (t.kind in tyMetaTypes or (t.kind == tyStatic and t.n == nil)): result.flags.excl tfInstClearedFlags + when false: + if newDestructors: + result.assignment = nil + #result.destructor = nil + result.sink = nil + +template typeBound(c, newty, oldty, field, info) = + let opr = newty.field + if opr != nil and sfFromGeneric notin opr.flags: + # '=' needs to be instantiated for generics when the type is constructed: + newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvocation[A, tyGenericInvocation[A, B]] @@ -357,16 +368,10 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = assert newbody.kind in {tyRef, tyPtr} assert newbody.lastSon.typeInst == nil newbody.lastSon.typeInst = result - template typeBound(field) = - let opr = newbody.field - if opr != nil and sfFromGeneric notin opr.flags: - # '=' needs to be instantiated for generics when the type is constructed: - newbody.field = cl.c.instTypeBoundOp(cl.c, opr, result, cl.info, - attachedAsgn, 1) if newDestructors: - typeBound(destructor) - typeBound(sink) - typeBound(assignment) + cl.c.typesWithOps.add((newbody, result)) + else: + typeBound(cl.c, newbody, result, assignment, cl.info) let methods = skipTypes(bbody, abstractPtrs).methods for col, meth in items(methods): # we instantiate the known methods belonging to that type, this causes @@ -534,6 +539,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard +proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = + if not newDestructors: return + var i = 0 + while i < c.typesWithOps.len: + let (newty, oldty) = c.typesWithOps[i] + typeBound(c, newty, oldty, destructor, info) + typeBound(c, newty, oldty, sink, info) + typeBound(c, newty, oldty, assignment, info) + inc i + setLen(c.typesWithOps, 0) + proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = initIdTable(result.symMap) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 25981b826..b4e79e7e9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2299,7 +2299,8 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) - assert sfFromGeneric in result.flags + if op == attachedDeepCopy: + assert sfFromGeneric in result.flags include suggest diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim index 45f2f66e6..924644392 100644 --- a/tests/destructor/topttree.nim +++ b/tests/destructor/topttree.nim @@ -3,7 +3,11 @@ discard """ 60.0 90.0 120.0 -4 4''' +10.0 +60.0 +90.0 +120.0 +8 8''' cmd: '''nim c --newruntime $file''' """ @@ -18,8 +22,8 @@ var proc `=destroy`*[T](x: var opt[T]) = if x.data != nil: - #when not supportsCopyMem(T): - `=destroy`(x.data[]) + when not supportsCopyMem(T): + `=destroy`(x.data[]) dealloc(x.data) inc deallocCount x.data = nil @@ -93,6 +97,8 @@ proc main = insert t, 10.0 insert t, 120.0 write t + let copy = t + write copy main() echo allocCount, " ", deallocCount -- cgit 1.4.1-2-gfad0 From f1dab3908699aef2978b428ed9c15086a2c4bf8c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 29 Oct 2017 20:36:07 +0100 Subject: remove old implementation of the roof operator; make tests green again; close #6292 --- compiler/semdata.nim | 1 - compiler/semexprs.nim | 11 +---------- compiler/semmagic.nim | 32 +------------------------------- compiler/semtempl.nim | 18 ------------------ lib/impure/nre.nim | 36 +++++++++++++++++------------------- lib/pure/matchers.nim | 2 +- lib/system.nim | 2 +- tests/array/troof1.nim | 27 +-------------------------- tests/array/troof2.nim | 10 ---------- tests/array/troof3.nim | 5 ++--- tests/array/troof4.nim | 37 ------------------------------------- tests/stdlib/nre/captures.nim | 6 +++--- tests/stdlib/nre/find.nim | 2 +- 13 files changed, 28 insertions(+), 161 deletions(-) delete mode 100644 tests/array/troof2.nim delete mode 100644 tests/array/troof4.nim (limited to 'compiler/semdata.nim') diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b6b5db101..3e57d1104 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -37,7 +37,6 @@ type # in standalone ``except`` and ``finally`` next*: PProcCon # used for stacking procedure contexts wasForwarded*: bool # whether the current proc has a separate header - bracketExpr*: PNode # current bracket expression (for ^ support) mapping*: TIdTable TMatchedConcept* = object diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 195625489..c3aead18e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1189,7 +1189,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = tyCString: if n.len != 2: return nil n.sons[0] = makeDeref(n.sons[0]) - c.p.bracketExpr = n.sons[0] for i in countup(1, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efInTypeof, efDetermineType}) @@ -1210,7 +1209,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of tyTuple: if n.len != 2: return nil n.sons[0] = makeDeref(n.sons[0]) - c.p.bracketExpr = n.sons[0] # [] operator for tuples requires constant expression: n.sons[1] = semConstExpr(c, n.sons[1]) if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in @@ -1248,17 +1246,13 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of skType: result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) else: - c.p.bracketExpr = n.sons[0] - else: - c.p.bracketExpr = n.sons[0] + discard proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = - let oldBracketExpr = c.p.bracketExpr result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) - c.p.bracketExpr = oldBracketExpr proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = var id = considerQuotedIdent(a[1], a) @@ -1330,7 +1324,6 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = of nkBracketExpr: # a[i] = x # --> `[]=`(a, i, x) - let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) if a == nil: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") @@ -1340,9 +1333,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = return n else: result = semExprNoType(c, result) - c.p.bracketExpr = oldBracketExpr return result - c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index ba19e0865..d721f42ab 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -38,9 +38,7 @@ proc skipAddr(n: PNode): PNode {.inline.} = proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = result = newNodeI(nkBracketExpr, n.info) for i in 1..\w)").captures["letter"] == "a"`` ## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"`` ## - ## ``captureBounds[]: Option[Slice[int]]`` + ## ``captureBounds[]: Option[Slice[int, int]]`` ## gets the bounds of the given capture according to the same rules as ## the above. If the capture is not filled, then ``None`` is returned. ## The bounds are both inclusive. @@ -167,7 +167,7 @@ type ## ``match: string`` ## the full text of the match. ## - ## ``matchBounds: Slice[int]`` + ## ``matchBounds: Slice[int, int]`` ## the bounds of the match, as in ``captureBounds[]`` ## ## ``(captureBounds|captures).toTable`` @@ -182,9 +182,9 @@ type ## Not nil. str*: string ## The string that was matched against. ## Not nil. - pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match - ## Other items are the captures - ## `a` is inclusive start, `b` is exclusive end + pcreMatchBounds: seq[Slice[cint, cint]] ## First item is the bounds of the match + ## Other items are the captures + ## `a` is inclusive start, `b` is exclusive end Captures* = distinct RegexMatch CaptureBounds* = distinct RegexMatch @@ -251,13 +251,13 @@ proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(p proc captures*(pattern: RegexMatch): Captures = return Captures(pattern) -proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] = +proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int, int]] = let pattern = RegexMatch(pattern) if pattern.pcreMatchBounds[i + 1].a != -1: let bounds = pattern.pcreMatchBounds[i + 1] return some(int(bounds.a) .. int(bounds.b-1)) else: - return none(Slice[int]) + return none(Slice[int, int]) proc `[]`*(pattern: Captures, i: int): string = let pattern = RegexMatch(pattern) @@ -272,10 +272,10 @@ proc `[]`*(pattern: Captures, i: int): string = proc match*(pattern: RegexMatch): string = return pattern.captures[-1] -proc matchBounds*(pattern: RegexMatch): Slice[int] = +proc matchBounds*(pattern: RegexMatch): Slice[int, int] = return pattern.captureBounds[-1].get -proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] = +proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int, int]] = let pattern = RegexMatch(pattern) return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)] @@ -295,13 +295,13 @@ proc toTable*(pattern: Captures, default: string = nil): Table[string, string] = result = initTable[string, string]() toTableImpl(nextVal == nil) -proc toTable*(pattern: CaptureBounds, default = none(Slice[int])): - Table[string, Option[Slice[int]]] = - result = initTable[string, Option[Slice[int]]]() +proc toTable*(pattern: CaptureBounds, default = none(Slice[int, int])): + Table[string, Option[Slice[int, int]]] = + result = initTable[string, Option[Slice[int, int]]]() toTableImpl(nextVal.isNone) template itemsImpl(cond: untyped) {.dirty.} = - for i in 0 .. foo)(?bar)?")) check(ex1.captureBounds["foo"] == some(0..2)) - check(ex1.captureBounds["bar"] == none(Slice[int])) + check(ex1.captureBounds["bar"] == none(Slice[int, int])) test "capture count": let ex1 = re("(?foo)(?bar)?") @@ -42,7 +42,7 @@ suite "captures": test "named capture table": let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable()) - check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable()) + check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int, int])}.toTable()) check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable()) let ex2 = "foobar".find(re("(?foo)(?bar)?")) @@ -51,7 +51,7 @@ suite "captures": test "capture sequence": let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toSeq == @["foo", nil]) - check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) + check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int, int])]) check(ex1.captures.toSeq("") == @["foo", ""]) let ex2 = "foobar".find(re("(?foo)(?bar)?")) diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim index caa953ff4..c37ac56ba 100644 --- a/tests/stdlib/nre/find.nim +++ b/tests/stdlib/nre/find.nim @@ -12,7 +12,7 @@ suite "find": test "find bounds": check(toSeq(findIter("1 2 3 4 5 ", re" ")).map( - proc (a: RegexMatch): Slice[int] = a.matchBounds + proc (a: RegexMatch): Slice[int, int] = a.matchBounds ) == @[1..1, 3..3, 5..5, 7..7, 9..9]) test "overlapping find": -- cgit 1.4.1-2-gfad0 From 7c03c882f55d7c03ce94943d0d8f622c6904a334 Mon Sep 17 00:00:00 2001 From: Fabian Keller Date: Tue, 31 Oct 2017 19:48:01 +0100 Subject: Remove more usages of unary lt (fixes #6634) (#6641) * fixes #6634 * remove more usages of unary < --- compiler/semdata.nim | 2 +- lib/pure/securehash.nim | 2 +- lib/system/debugger.nim | 4 ++-- lib/system/repr.nim | 2 +- lib/system/sysspawn.nim | 2 +- nimsuggest/sexp.nim | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'compiler/semdata.nim') diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 3e57d1104..5057260a4 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -339,7 +339,7 @@ proc makeNotType*(c: PContext, t1: PType): PType = proc nMinusOne*(n: PNode): PNode = result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic("<", mUnaryLt)), + newSymNode(getSysMagic("pred", mPred)), n]) # Remember to fix the procs below this one when you make changes! diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim index c19146669..57c1f3631 100644 --- a/lib/pure/securehash.nim +++ b/lib/pure/securehash.nim @@ -181,7 +181,7 @@ proc `$`*(self: SecureHash): string = result.add(toHex(int(v), 2)) proc parseSecureHash*(hash: string): SecureHash = - for i in 0.. Date: Sun, 26 Nov 2017 02:51:11 +0100 Subject: the documentation generator now supports system.runnableExamples --- changelog.md | 4 ++ compiler/ast.nim | 2 +- compiler/condsyms.nim | 1 + compiler/docgen.nim | 139 +++++++++++++++++++++++---------------- compiler/sem.nim | 13 ++++ compiler/semdata.nim | 1 + compiler/semexprs.nim | 11 ++++ lib/packages/docutils/rstgen.nim | 2 +- lib/system.nim | 17 +++++ 9 files changed, 132 insertions(+), 58 deletions(-) (limited to 'compiler/semdata.nim') diff --git a/changelog.md b/changelog.md index 49cd4123a..14352374c 100644 --- a/changelog.md +++ b/changelog.md @@ -94,3 +94,7 @@ This now needs to be written as: - [``poly``](https://github.com/lcrees/polynumeric) - [``pdcurses``](https://github.com/lcrees/pdcurses) - [``romans``](https://github.com/lcrees/romans) + +- Added ``system.runnableExamples`` to make examples in Nim's documentation easier + to write and test. The examples are tested as the last step of + ``nim doc``. diff --git a/compiler/ast.nim b/compiler/ast.nim index 787cb4997..5bf4184c9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -639,7 +639,7 @@ type mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNGenSym, - mNimvm, mIntDefine, mStrDefine + mNimvm, mIntDefine, mStrDefine, mRunnableExamples # things that we can evaluate safely at compile time, even if not asked for it: const diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 2050a746b..4879ce5c3 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -110,3 +110,4 @@ proc initDefines*() = when false: defineSymbol("nimHasOpt") defineSymbol("nimNoArrayToCstringConversion") defineSymbol("nimNewRoof") + defineSymbol("nimHasRunnableExamples") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8978052e2..4a3674812 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -204,10 +204,85 @@ proc getPlainDocstring(n: PNode): string = if n.comment != nil and startsWith(n.comment, "##"): result = n.comment if result.len < 1: - if n.kind notin {nkEmpty..nkNilLit}: - for i in countup(0, len(n)-1): - result = getPlainDocstring(n.sons[i]) - if result.len > 0: return + for i in countup(0, safeLen(n)-1): + result = getPlainDocstring(n.sons[i]) + if result.len > 0: return + +proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}) = + var r: TSrcGen + var literal = "" + initTokRender(r, n, renderFlags) + var kind = tkEof + while true: + getNextTok(r, kind, literal) + case kind + of tkEof: + break + of tkComment: + dispA(result, "$1", "\\spanComment{$1}", + [rope(esc(d.target, literal))]) + of tokKeywordLow..tokKeywordHigh: + dispA(result, "$1", "\\spanKeyword{$1}", + [rope(literal)]) + of tkOpr: + dispA(result, "$1", "\\spanOperator{$1}", + [rope(esc(d.target, literal))]) + of tkStrLit..tkTripleStrLit: + dispA(result, "$1", + "\\spanStringLit{$1}", [rope(esc(d.target, literal))]) + of tkCharLit: + dispA(result, "$1", "\\spanCharLit{$1}", + [rope(esc(d.target, literal))]) + of tkIntLit..tkUInt64Lit: + dispA(result, "$1", + "\\spanDecNumber{$1}", [rope(esc(d.target, literal))]) + of tkFloatLit..tkFloat128Lit: + dispA(result, "$1", + "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) + of tkSymbol: + dispA(result, "$1", + "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) + of tkSpaces, tkInvalid: + add(result, literal) + of tkCurlyDotLe: + dispA(result, """$1
""", + "\\spanOther{$1}", + [rope(esc(d.target, literal))]) + of tkCurlyDotRi: + dispA(result, "
$1", + "\\spanOther{$1}", + [rope(esc(d.target, literal))]) + of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, tkParDotLe, + tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, + tkAccent, tkColonColon, + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: + dispA(result, "$1", "\\spanOther{$1}", + [rope(esc(d.target, literal))]) + +proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) = + case n.kind + of nkCallKinds: + if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and + n.len >= 2 and n.lastSon.kind == nkStmtList: + dispA(dest, "\n$1\n", + "\n\\textbf{$1}\n", [rope"Examples:"]) + inc d.listingCounter + let id = $d.listingCounter + dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"]) + # this is a rather hacky way to get rid of the initial indentation + # that the renderer currently produces: + var i = 0 + var body = n.lastSon + if body.len == 1 and body.kind == nkStmtList: body = body.lastSon + for b in body: + if i > 0: dest.add "\n" + inc i + nodeToHighlightedHtml(d, b, dest, {}) + dest.add(d.config.getOrDefault"doc.listing_end" % id) + else: discard + for i in 0 ..< n.safeLen: + getAllRunnableExamples(d, n[i], dest) when false: proc findDocComment(n: PNode): PNode = @@ -379,11 +454,12 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = let name = getName(d, nameNode) nameRope = name.rope - plainDocstring = getPlainDocstring(n) # call here before genRecComment! + var plainDocstring = getPlainDocstring(n) # call here before genRecComment! var result: Rope = nil var literal, plainName = "" var kind = tkEof var comm = genRecComment(d, n) # call this here for the side-effect! + getAllRunnableExamples(d, n, comm) var r: TSrcGen # Obtain the plain rendered string for hyperlink titles. initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, @@ -395,53 +471,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = plainName.add(literal) # Render the HTML hyperlink. - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - while true: - getNextTok(r, kind, literal) - case kind - of tkEof: - break - of tkComment: - dispA(result, "$1", "\\spanComment{$1}", - [rope(esc(d.target, literal))]) - of tokKeywordLow..tokKeywordHigh: - dispA(result, "$1", "\\spanKeyword{$1}", - [rope(literal)]) - of tkOpr: - dispA(result, "$1", "\\spanOperator{$1}", - [rope(esc(d.target, literal))]) - of tkStrLit..tkTripleStrLit: - dispA(result, "$1", - "\\spanStringLit{$1}", [rope(esc(d.target, literal))]) - of tkCharLit: - dispA(result, "$1", "\\spanCharLit{$1}", - [rope(esc(d.target, literal))]) - of tkIntLit..tkUInt64Lit: - dispA(result, "$1", - "\\spanDecNumber{$1}", [rope(esc(d.target, literal))]) - of tkFloatLit..tkFloat128Lit: - dispA(result, "$1", - "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) - of tkSymbol: - dispA(result, "$1", - "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) - of tkSpaces, tkInvalid: - add(result, literal) - of tkCurlyDotLe: - dispA(result, """$1
""", - "\\spanOther{$1}", - [rope(esc(d.target, literal))]) - of tkCurlyDotRi: - dispA(result, "
$1", - "\\spanOther{$1}", - [rope(esc(d.target, literal))]) - of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, tkParDotLe, - tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, - tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: - dispA(result, "$1", "\\spanOther{$1}", - [rope(esc(d.target, literal))]) + nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments}) inc(d.id) let @@ -609,10 +639,7 @@ proc generateJson*(d: PDoc, n: PNode) = else: discard proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string = - var - name = getName(d, nameNode) - - result = name & "\n" + result = getName(d, nameNode) & "\n" proc generateTags*(d: PDoc, n: PNode, r: var Rope) = case n.kind diff --git a/compiler/sem.nim b/compiler/sem.nim index 3608bc11c..495321de4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -570,6 +570,18 @@ proc myProcess(context: PPassContext, n: PNode): PNode = result = ast.emptyNode #if gCmd == cmdIdeTools: findSuggest(c, n) +proc testExamples(c: PContext) = + let inp = toFullPath(c.module.info) + let outp = inp.changeFileExt"" & "_examples.nim" + renderModule(c.runnableExamples, inp, outp) + let backend = if isDefined("js"): "js" + elif isDefined("cpp"): "cpp" + elif isDefined("objc"): "objc" + else: "c" + if os.execShellCmd("nim " & backend & " -r " & outp) != 0: + quit "[Examples] failed" + removeFile(outp) + proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) if gCmd == cmdIdeTools and not c.suggestionsMade: @@ -584,5 +596,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = result.add(c.module.ast) popOwner(c) popProcCon(c) + if c.runnableExamples != nil: testExamples(c) const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5057260a4..8affee649 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -136,6 +136,7 @@ type # the generic type has been constructed completely. See # tests/destructor/topttree.nim for an example that # would otherwise fail. + runnableExamples*: PNode proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d600b1c48..380b367bc 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1847,6 +1847,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) + of mRunnableExamples: + if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: + if sfMainModule in c.module.flags: + let inp = toFullPath(c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + c.runnableExamples.add newTree(nkBlockStmt, emptyNode, n.lastSon) + result = n + else: + result = emptyNode else: result = semDirectOp(c, n, flags) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 1272affdc..f156c440b 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -46,7 +46,7 @@ type target*: OutputTarget config*: StringTableRef splitAfter*: int # split too long entries in the TOC - listingCounter: int + listingCounter*: int tocPart*: seq[TocEntry] hasToc*: bool theIndex: string # Contents of the index file to be dumped at the end. diff --git a/lib/system.nim b/lib/system.nim index 1b53bf9f5..323ff00e6 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3992,3 +3992,20 @@ when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage): proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32", importc: "SetConsoleOutputCP".} discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage + + +when defined(nimHasRunnableExamples): + proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".} + ## A section you should use to mark `runnable example`:idx: code with. + ## + ## - In normal debug and release builds code within + ## a ``runnableExamples`` section is ignored. + ## - The documentation generator is aware of these examples and considers them + ## part of the ``##`` doc comment. As the last step of documentation + ## generation the examples are put into an ``$file_example.nim`` file, + ## compiled and tested. The collected examples are + ## put into their own module to ensure the examples do not refer to + ## non-exported symbols. +else: + template runnableExamples*(body: untyped) = + discard -- cgit 1.4.1-2-gfad0