diff options
-rw-r--r-- | compiler/docgen.nim | 1 | ||||
-rw-r--r-- | compiler/msgs.nim | 7 | ||||
-rw-r--r-- | doc/tut2.txt | 102 | ||||
-rw-r--r-- | lib/packages/docutils/rst.nim | 86 | ||||
-rw-r--r-- | lib/packages/docutils/rstgen.nim | 117 | ||||
-rw-r--r-- | lib/system.nim | 49 |
6 files changed, 267 insertions, 95 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8bd0bb490..35acf1379 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -41,6 +41,7 @@ proc compilerMsgHandler(filename: string, line, col: int, of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnsupportedLanguage: k = warnLanguageXNotSupported + of mwUnsupportedField: k = warnFieldXNotSupported globalError(newLineInfo(filename, line, col), k, arg) proc docgenFindFile(s: string): string {.procvar.} = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 51d2836f8..d40d94a71 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -114,7 +114,8 @@ type warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnDeprecated, warnConfigDeprecated, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, @@ -375,6 +376,7 @@ const warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", + warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", @@ -415,7 +417,8 @@ const "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", + "RedefinitionOfLabel", "UnknownSubstitutionX", + "LanguageXNotSupported", "FieldXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", diff --git a/doc/tut2.txt b/doc/tut2.txt index 34f59b1cb..d8f9071a9 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -791,9 +791,9 @@ to be included along the program containing the license information:: The ``readCfgAtRuntime`` proc will open the given filename and return a ``Table`` from the `tables module <tables.html>`_. The parsing of the file is done (without much care for handling invalid data or corner cases) using the -``splitLines`` proc from the `strutils module <strutils.html>`_. There are many -things which can fail; mind the purpose is explaining how to make this run at -compile time, not how to properly implement a DRM scheme. +`splitLines proc from the strutils module <strutils.html#splitLines>`_. There +are many things which can fail; mind the purpose is explaining how to make +this run at compile time, not how to properly implement a DRM scheme. The reimplementation of this code as a compile time proc will allow us to get rid of the ``data.cfg`` file we would need to distribute along the binary, plus @@ -816,6 +816,8 @@ time string with the *generated source code*, which we then pass to the modified source code implementing the macro: .. code-block:: nim + :number-lines: + import macros, strutils macro readCfgAndBuildSource(cfgFilename: string): stmt = @@ -842,29 +844,31 @@ modified source code implementing the macro: echo cfglicenseKey echo cfgversion -The good news is not much has changed! First, we need to change the handling of -the input parameter. In the dynamic version the ``readCfgAtRuntime`` proc -receives a string parameter. However, in the macro version it is also declared -as string, but this is the *outside* interface of the macro. When the macro is -run, it actually gets a ``PNimNode`` object instead of a string, and we have -to call the ``strVal`` proc from the `macros module <macros.html>`_ to obtain -the string being passed in to the macro. - -Second, we cannot use the ``readFile`` proc from the `system module -<system.html>`_ due to FFI restriction at compile time. If we try to use this -proc, or any other which depends on FFI, the compiler will error with the -message ``cannot evaluate`` and a dump of the macro's source code, along with a -stack trace where the compiler reached before bailing out. We can get around -this limitation by using the ``slurp`` proc from the `system module -<system.html>`_, which was precisely made for compilation time (just like -``gorge`` which executes an external program and captures its output). - -The interesting thing is that our macro does not return a runtime ``Table`` -object. Instead, it builds up Nim source code into the ``source`` variable. -For each line of the configuration file a ``const`` variable will be generated. -To avoid conflicts we prefix these variables with ``cfg``. In essence, what the -compiler is doing is replacing the line calling the macro with the following -snippet of code: +The good news is not much has changed! First, we need to change the handling +of the input parameter (line 3). In the dynamic version the +``readCfgAtRuntime`` proc receives a string parameter. However, in the macro +version it is also declared as string, but this is the *outside* interface of +the macro. When the macro is run, it actually gets a ``PNimNode`` object +instead of a string, and we have to call the `strVal proc +<macros.html#strVal>`_ (line 5) from the `macros module <macros.html>`_ to +obtain the string being passed in to the macro. + +Second, we cannot use the `readFile proc <system.html#readFile>`_ from the +`system module <system.html>`_ due to FFI restriction at compile time. If we +try to use this proc, or any other which depends on FFI, the compiler will +error with the message ``cannot evaluate`` and a dump of the macro's source +code, along with a stack trace where the compiler reached before bailing out. +We can get around this limitation by using the `slurp proc +<system.html#slurp>`_ from the `system module <system.html>`_, which was +precisely made for compilation time (just like `gorge <system.html#gorge>`_ +which executes an external program and captures its output). + +The interesting thing is that our macro does not return a runtime `Table +<tables.html#TTable>`_ object. Instead, it builds up Nim source code into +the ``source`` variable. For each line of the configuration file a ``const`` +variable will be generated (line 15). To avoid conflicts we prefix these +variables with ``cfg``. In essence, what the compiler is doing is replacing +the line calling the macro with the following snippet of code: .. code-block:: nim const cfgversion= "1.1" @@ -873,13 +877,14 @@ snippet of code: You can verify this yourself adding the line ``echo source`` somewhere at the end of the macro and compiling the program. Another difference is that instead -of calling the usual ``quit`` proc to abort (which we could still call) this -version calls the ``error`` proc. The ``error`` proc has the same behavior as -``quit`` but will dump also the source and file line information where the -error happened, making it easier for the programmer to find where compilation -failed. In this situation it would point to the line invoking the macro, but -**not** the line of ``data.cfg`` we are processing, that's something the macro -itself would need to control. +of calling the usual `quit proc <system.html#quit>`_ to abort (which we could +still call) this version calls the `error proc <macros.html#error>`_ (line +14). The ``error`` proc has the same behavior as ``quit`` but will dump also +the source and file line information where the error happened, making it +easier for the programmer to find where compilation failed. In this situation +it would point to the line invoking the macro, but **not** the line of +``data.cfg`` we are processing, that's something the macro itself would need +to control. Generating AST by hand @@ -887,11 +892,11 @@ Generating AST by hand To generate an AST we would need to intimately know the structures used by the Nim compiler exposed in the `macros module <macros.html>`_, which at first -look seems a daunting task. But we can use as helper shortcut the ``dumpTree`` -macro, which is used as a statement macro instead of an expression macro. -Since we know that we want to generate a bunch of ``const`` symbols we can -create the following source file and compile it to see what the compiler -*expects* from us: +look seems a daunting task. But we can use as helper shortcut the `dumpTree +macro <macros.html#dumpTree>`_, which is used as a statement macro instead of +an expression macro. Since we know that we want to generate a bunch of +``const`` symbols we can create the following source file and compile it to +see what the compiler *expects* from us: .. code-block:: nim import macros @@ -937,6 +942,8 @@ with this knowledge, let's look at the finished version of the AST building macro: .. code-block:: nim + :number-lines: + import macros, strutils macro readCfgAndBuildAST(cfgFilename: string): stmt = @@ -972,19 +979,20 @@ Since we are building on the previous example generating source code, we will only mention the differences to it. Instead of creating a temporary ``string`` variable and writing into it source code as if it were written *by hand*, we use the ``result`` variable directly and create a statement list node -(``nnkStmtList``) which will hold our children. +(``nnkStmtList``) which will hold our children (line 7). For each input line we have to create a constant definition (``nnkConstDef``) and wrap it inside a constant section (``nnkConstSection``). Once these -variables are created, we fill them hierarchichally like the previous AST dump -tree showed: the constant definition is a child of the section definition, and -the constant definition has an identifier node, an empty node (we let the -compiler figure out the type), and a string literal with the value. +variables are created, we fill them hierarchichally (line 17) like the +previous AST dump tree showed: the constant definition is a child of the +section definition, and the constant definition has an identifier node, an +empty node (we let the compiler figure out the type), and a string literal +with the value. A last tip when writing a macro: if you are not sure the AST you are building looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use it *inside* the macro you are writting/debugging. Instead ``echo`` the string -generated by ``treeRepr``. If at the end of the this example you add ``echo -treeRepr(result)`` you should get the same output as using the ``dumpTree`` -macro, but of course you can call that at any point of the macro where you -might be having troubles. +generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this +example you add ``echo treeRepr(result)`` you should get the same output as +using the ``dumpTree`` macro, but of course you can call that at any point of +the macro where you might be having troubles. diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b21d51c93..07014c754 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -39,7 +39,8 @@ type meInvalidDirective, mwRedefinitionOfLabel, mwUnknownSubstitution, - mwUnsupportedLanguage + mwUnsupportedLanguage, + mwUnsupportedField TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind, arg: string) {.nimcall.} ## what to do in case of an error @@ -55,7 +56,8 @@ const meInvalidDirective: "invalid directive: '$1'", mwRedefinitionOfLabel: "redefinition of label '$1'", mwUnknownSubstitution: "unknown substitution '$1'", - mwUnsupportedLanguage: "language '$1' not supported" + mwUnsupportedLanguage: "language '$1' not supported", + mwUnsupportedField: "field '$1' not supported" ] proc rstnodeToRefname*(n: PRstNode): string @@ -850,13 +852,13 @@ proc parseComment(p: var TRstParser): PRstNode = type TDirKind = enum # must be ordered alphabetically! - dkNone, dkAuthor, dkAuthors, dkCodeBlock, dkContainer, dkContents, + dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle const - DirIds: array[0..11, string] = ["", "author", "authors", "code-block", - "container", "contents", "figure", "image", "include", "index", "raw", - "title"] + DirIds: array[0..12, string] = ["", "author", "authors", "code", + "code-block", "container", "contents", "figure", "image", "include", + "index", "raw", "title"] proc getDirKind(s: string): TDirKind = let i = find(DirIds, s) @@ -876,7 +878,10 @@ proc parseUntilNewline(p: var TRstParser, father: PRstNode) = of tkEof, tkIndent: break proc parseSection(p: var TRstParser, result: PRstNode) -proc parseField(p: var TRstParser): PRstNode = +proc parseField(p: var TRstParser): PRstNode = + ## Returns a parsed rnField node. + ## + ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. result = newRstNode(rnField) var col = p.tok[p.idx].col var fieldname = newRstNode(rnFieldName) @@ -892,7 +897,11 @@ proc parseField(p: var TRstParser): PRstNode = add(result, fieldname) add(result, fieldbody) -proc parseFields(p: var TRstParser): PRstNode = +proc parseFields(p: var TRstParser): PRstNode = + ## Parses fields for a section or directive block. + ## + ## This proc may return nil if the parsing doesn't find anything of value, + ## otherwise it will return a node of rnFieldList type with children. result = nil var atStart = p.idx == 0 and p.tok[0].symbol == ":" if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or @@ -908,6 +917,18 @@ proc parseFields(p: var TRstParser): PRstNode = else: break +proc getFieldValue*(n: PRstNode): string = + ## Returns the value of a specific ``rnField`` node. + ## + ## This proc will assert if the node is not of the expected type. The empty + ## string will be returned as a minimum. Any value in the rst will be + ## stripped form leading/trailing whitespace. + assert n.kind == rnField + assert n.len == 2 + assert n.sons[0].kind == rnFieldName + assert n.sons[1].kind == rnFieldBody + result = addNodes(n.sons[1]).strip + proc getFieldValue(n: PRstNode, fieldname: string): string = result = "" if n.sons[1] == nil: return @@ -1387,7 +1408,16 @@ type TDirFlags = set[TDirFlag] TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.} -proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = +proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = + ## Parses arguments and options for a directive block. + ## + ## A directive block will always have three sons: the arguments for the + ## directive (rnDirArg), the options (rnFieldList) and the block + ## (rnLineBlock). This proc parses the two first nodes, the block is left to + ## the outer `parseDirective` call. + ## + ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to + ## check them before accessing. result = newRstNode(rnDirective) var args: PRstNode = nil var options: PRstNode = nil @@ -1421,6 +1451,9 @@ proc indFollows(p: TRstParser): bool = proc parseDirective(p: var TRstParser, flags: TDirFlags, contentParser: TSectionParser): PRstNode = + ## Returns a generic rnDirective tree. + ## + ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. result = parseDirective(p, flags) if not isNil(contentParser) and indFollows(p): pushInd(p, p.tok[p.idx].ival) @@ -1474,7 +1507,23 @@ proc dirInclude(p: var TRstParser): PRstNode = # InternalError("Too many binary zeros in include file") result = parseDoc(q) -proc dirCodeBlock(p: var TRstParser): PRstNode = +proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode = + ## Parses a code block. + ## + ## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the + ## description of ``parseDirective`` for further structure information. + ## + ## Code blocks can come in two forms, the standard `code directive + ## <http://docutils.sourceforge.net/docs/ref/rst/directives.html#code>`_ and + ## the nimrod extension ``.. code-block::``. If the block is an extension, we + ## want the default language syntax highlighting to be Nimrod, so we create a + ## fake internal field to comminicate with the generator. The field is named + ## ``default-language``, which is unlikely to collide with a field specified + ## by any random rst input file. + ## + ## As an extension this proc will process the ``file`` extension field and if + ## present will replace the code block with the contents of the referenced + ## file. result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock) var filename = strip(getFieldValue(result, "file")) if filename != "": @@ -1483,6 +1532,20 @@ proc dirCodeBlock(p: var TRstParser): PRstNode = var n = newRstNode(rnLiteralBlock) add(n, newRstNode(rnLeaf, readFile(path))) result.sons[2] = n + + # Extend the field block if we are using our custom extension. + if nimrodExtension: + # Create a field block if the input block didn't have any. + if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) + assert result.sons[1].kind == rnFieldList + # Hook the extra field and specify the Nimrod language as value. + var extraNode = newRstNode(rnField) + extraNode.add(newRstNode(rnFieldName)) + extraNode.add(newRstNode(rnFieldBody)) + extraNode.sons[0].add(newRstNode(rnLeaf, "default-language")) + extraNode.sons[1].add(newRstNode(rnLeaf, "Nimrod")) + result.sons[1].add(extraNode) + result.kind = rnCodeBlock proc dirContainer(p: var TRstParser): PRstNode = @@ -1566,7 +1629,8 @@ proc parseDotDot(p: var TRstParser): PRstNode = result = dirRaw(p) else: rstMessage(p, meInvalidDirective, d) - of dkCodeBlock: result = dirCodeBlock(p) + of dkCode: result = dirCodeBlock(p) + of dkCodeBlock: result = dirCodeBlock(p, nimrodExtension = true) of dkIndex: result = dirIndex(p) else: rstMessage(p, meInvalidDirective, d) popInd(p) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 02b0afd2f..cfbc4a708 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -24,7 +24,7 @@ ## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, - algorithm + algorithm, parseutils const HtmlExt = "html" @@ -63,6 +63,19 @@ type PDoc = var TRstGenerator ## Alias to type less. + CodeBlockParams = object ## Stores code block params. + numberLines: bool ## True if the renderer has to show line numbers. + startLine: int ## The starting line of the code block, by default 1. + langStr: string ## Input string used to specify the language. + lang: TSourceLanguage ## Type of highlighting, by default none. + + +proc init(p: var CodeBlockParams) = + ## Default initialisation of CodeBlockParams to sane values. + p.startLine = 1 + p.lang = langNone + p.langStr = "" + proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, config: StringTableRef, filename: string, options: TRstParseOptions, @@ -761,26 +774,102 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = height="17" hspace="2" vspace="2" />""", "\\includegraphics{$1}", [n.text]) +proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = + ## Parses useful fields which can appear before a code block. + ## + ## This supports the special ``default-language`` internal string generated + ## by the ``rst`` module to communicate a specific default language. + case n.getArgument.toLower + of "number-lines": + params.numberLines = true + # See if the field has a parameter specifying a different line than 1. + var number: int + if parseInt(n.getFieldValue, number) > 0: + params.startLine = number + of "file": + # The ``file`` option is a Nimrod extension to the official spec, it acts + # like it would for other directives like ``raw`` or ``cvs-table``. This + # field is dealt with in ``rst.nim`` which replaces the existing block with + # the referenced file, so we only need to ignore it here to avoid incorrect + # warning messages. + discard + of "default-language": + params.langStr = n.getFieldValue.strip + params.lang = params.langStr.getSourceLanguage + else: + d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument) + +proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = + ## Iterates over all code block fields and returns processed params. + ## + ## Also processes the argument of the directive as the default language. This + ## is done last so as to override any internal communication field variables. + result.init + if n.isNil: + return + assert n.kind == rnCodeBlock + assert(not n.sons[2].isNil) + + # Parse the field list for rendering parameters if there are any. + if not n.sons[1].isNil: + for son in n.sons[1].sons: d.parseCodeBlockField(son, result) + + # Parse the argument and override the language. + result.langStr = strip(getArgument(n)) + if result.langStr != "": + result.lang = getSourceLanguage(result.langStr) + +proc buildLinesHTMLTable(params: CodeBlockParams, code: string): + tuple[beginTable, endTable: string] = + ## Returns the necessary tags to start/end a code block in HTML. + ## + ## If the numberLines has not been used, the tags will default to a simple + ## <pre> pair. Otherwise it will build a table and insert an initial column + ## with all the line numbers, which requires you to pass the `code` to detect + ## how many lines have to be generated (and starting at which point!). + if not params.numberLines: + result = ("<pre>", "</pre>") + return + + var codeLines = 1 + code.strip.countLines + assert codeLines > 0 + result.beginTable = """<table><tbody><tr><td class="blob-line-nums"><pre>""" + var line = params.startLine + while codeLines > 0: + result.beginTable.add($line & "\n") + line.inc + codeLines.dec + result.beginTable.add("</pre></td><td><pre>") + result.endTable = "</pre></td></tr></tbody></table>" + proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = + ## Renders a code block, appending it to `result`. + ## + ## If the code block uses the ``number-lines`` option, a table will be + ## generated with two columns, the first being a list of numbers and the + ## second the code block itself. The code block can use syntax highlighting, + ## which depends on the directive argument specified by the rst input, and + ## may also come from the parser through the internal ``default-language`` + ## option to differentiate between a plain code block and nimrod's code block + ## extension. + assert n.kind == rnCodeBlock if n.sons[2] == nil: return + var params = d.parseCodeBlockParams(n) var m = n.sons[2].sons[0] assert m.kind == rnLeaf - var langstr = strip(getArgument(n)) - var lang: TSourceLanguage - if langstr == "": - lang = langNim # default language - else: - lang = getSourceLanguage(langstr) - - dispA(d.target, result, "<pre>", "\\begin{rstpre}\n", []) - if lang == langNone: - d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr) + + let (blockStart, blockEnd) = params.buildLinesHTMLTable(m.text) + + dispA(d.target, result, blockStart, "\\begin{rstpre}\n", []) + if params.lang == langNone: + if len(params.langStr) > 0: + d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) for letter in m.text: escChar(d.target, result, letter) else: var g: TGeneralTokenizer initGeneralTokenizer(g, m.text) while true: - getNextToken(g, lang) + getNextToken(g, params.lang) case g.kind of gtEof: break of gtNone, gtWhitespace: @@ -790,8 +879,8 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = esc(d.target, substr(m.text, g.start, g.length+g.start-1)), tokenClassToStr[g.kind]]) deinitGeneralTokenizer(g) - dispA(d.target, result, "</pre>", "\n\\end{rstpre}\n") - + dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") + proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" renderRstToOut(d, n.sons[2], tmp) diff --git a/lib/system.nim b/lib/system.nim index a9b4da77a..7b78d1cf1 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1102,11 +1102,11 @@ else: const QuitSuccess* = 0 - ## is the value that should be passed to ``quit`` to indicate + ## is the value that should be passed to `quit <#quit>`_ to indicate ## success. QuitFailure* = 1 - ## is the value that should be passed to ``quit`` to indicate + ## is the value that should be passed to `quit <#quit>`_ to indicate ## failure. var programResult* {.exportc: "nim_program_result".}: int @@ -1119,10 +1119,11 @@ proc quit*(errorcode: int = QuitSuccess) {. ## Stops the program immediately with an exit code. ## ## Before stopping the program the "quit procedures" are called in the - ## opposite order they were added with ``addQuitProc``. ``quit`` never - ## returns and ignores any exception that may have been raised by the quit - ## procedures. It does *not* call the garbage collector to free all the - ## memory, unless a quit procedure calls ``GC_collect``. + ## opposite order they were added with `addQuitProc <#addQuitProc>`_. + ## ``quit`` never returns and ignores any exception that may have been raised + ## by the quit procedures. It does *not* call the garbage collector to free + ## all the memory, unless a quit procedure calls `GC_fullCollect + ## <#GC_fullCollect>`_. ## ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim ## program finishes without incident. A raised unhandled exception is @@ -1130,7 +1131,8 @@ proc quit*(errorcode: int = QuitSuccess) {. ## ## Note that this is a *runtime* call and using ``quit`` inside a macro won't ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the ``error`` or ``fatal`` pragmas. + ## macro, use the `error <manual.html#error-pragma>`_ or `fatal + ## <manual.html#fatal-pragma>`_ pragmas. template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): @@ -1284,11 +1286,12 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {. proc addQuitProc*(QuitProc: proc() {.noconv.}) {. importc: "atexit", header: "<stdlib.h>".} - ## adds/registers a quit procedure. Each call to ``addQuitProc`` - ## registers another quit procedure. Up to 30 procedures can be - ## registered. They are executed on a last-in, first-out basis - ## (that is, the last function registered is the first to be executed). - ## ``addQuitProc`` raises an EOutOfIndex if ``quitProc`` cannot be + ## Adds/registers a quit procedure. + ## + ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30 + ## procedures can be registered. They are executed on a last-in, first-out + ## basis (that is, the last function registered is the first to be executed). + ## ``addQuitProc`` raises an EOutOfIndex exception if ``QuitProc`` cannot be ## registered. # Support for addQuitProc() is done by Ansi C's facilities here. @@ -2349,9 +2352,12 @@ when not defined(JS): #and not defined(NimrodVM): ## current file position is not at the beginning of the file. proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} - ## Opens a file named `filename` for reading. Then calls `readAll` - ## and closes the file afterwards. Returns the string. - ## Raises an IO exception in case of an error. + ## Opens a file named `filename` for reading. + ## + ## Then calls `readAll <#readAll>`_ and closes the file afterwards. + ## Returns the string. Raises an IO exception in case of an error. If + ## you need to call this inside a compile time macro you can use + ## `staticRead <#staticRead>`_. proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} ## Opens a file named `filename` for writing. Then writes the @@ -2852,19 +2858,20 @@ proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = spliceImpl(s, a, L, b) proc slurp*(filename: string): string {.magic: "Slurp".} - ## This is an alias for ``staticRead``. + ## This is an alias for `staticRead <#staticRead>`_. proc staticRead*(filename: string): string {.magic: "Slurp".} - ## Compile-time ``readFile`` proc for easy `resource`:idx: embedding: + ## Compile-time `readFile <#readFile>`_ proc for easy `resource`:idx: + ## embedding: ## ## .. code-block:: nim ## const myResource = staticRead"mydatafile.bin" ## - ## ``slurp`` is an alias for ``staticRead``. + ## `slurp <#slurp>`_ is an alias for ``staticRead``. proc gorge*(command: string, input = ""): string {. magic: "StaticExec".} = discard - ## This is an alias for ``staticExec``. + ## This is an alias for `staticExec <#staticExec>`_. proc staticExec*(command: string, input = ""): string {. magic: "StaticExec".} = discard @@ -2876,8 +2883,8 @@ proc staticExec*(command: string, input = ""): string {. ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## "\nCompiled on " & staticExec("uname -v") ## - ## ``gorge`` is an alias for ``staticExec``. Note that you can use this proc - ## inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL + ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use + ## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL ## <nimc.html#passl-pragma>`_. proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} |