Merge branch 'pr_adds_code_directive_to_rst' into pr_bigbreak_adds_code_directive_to_rst

Conflicts:
	doc/tut2.txt
	lib/packages/docutils/rstgen.nim
	lib/system.nim
This commit is contained in:
Grzegorz Adam Hankiewicz 2014-11-03 15:50:05 +01:00
commit a7aa2de166
6 changed files with 265 additions and 93 deletions

View file

@ -41,6 +41,7 @@ proc compilerMsgHandler(filename: string, line, col: int,
of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported
globalError(newLineInfo(filename, line, col), k, arg) globalError(newLineInfo(filename, line, col), k, arg)
proc docgenFindFile(s: string): string {.procvar.} = proc docgenFindFile(s: string): string {.procvar.} =

View file

@ -114,7 +114,8 @@ type
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnDeprecated, warnConfigDeprecated, warnDeprecated, warnConfigDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored,
warnNilStatement, warnAnalysisLoophole, warnNilStatement, warnAnalysisLoophole,
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
@ -375,6 +376,7 @@ const
warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]",
warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]",
warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]",
warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]",
@ -415,7 +417,8 @@ const
"XIsNeverRead", "XmightNotBeenInit", "XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated", "Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "NilStmt", "CommentXIgnored", "NilStmt",
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",

View file

@ -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 The ``readCfgAtRuntime`` proc will open the given filename and return a
``Table`` from the `tables module <tables.html>`_. The parsing of the file is ``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 done (without much care for handling invalid data or corner cases) using the
``splitLines`` proc from the `strutils module <strutils.html>`_. There are many `splitLines proc from the strutils module <strutils.html#splitLines>`_. There
things which can fail; mind the purpose is explaining how to make this run at are many things which can fail; mind the purpose is explaining how to make
compile time, not how to properly implement a DRM scheme. 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 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 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: modified source code implementing the macro:
.. code-block:: nim .. code-block:: nim
:number-lines:
import macros, strutils import macros, strutils
macro readCfgAndBuildSource(cfgFilename: string): stmt = macro readCfgAndBuildSource(cfgFilename: string): stmt =
@ -842,29 +844,31 @@ modified source code implementing the macro:
echo cfglicenseKey echo cfglicenseKey
echo cfgversion echo cfgversion
The good news is not much has changed! First, we need to change the handling of The good news is not much has changed! First, we need to change the handling
the input parameter. In the dynamic version the ``readCfgAtRuntime`` proc of the input parameter (line 3). In the dynamic version the
receives a string parameter. However, in the macro version it is also declared ``readCfgAtRuntime`` proc receives a string parameter. However, in the macro
as string, but this is the *outside* interface of the macro. When the macro is version it is also declared as string, but this is the *outside* interface of
run, it actually gets a ``PNimNode`` object instead of a string, and we have the macro. When the macro is run, it actually gets a ``PNimNode`` object
to call the ``strVal`` proc from the `macros module <macros.html>`_ to obtain instead of a string, and we have to call the `strVal proc
the string being passed in to the macro. <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 from the `system module Second, we cannot use the `readFile proc <system.html#readFile>`_ from the
<system.html>`_ due to FFI restriction at compile time. If we try to use this `system module <system.html>`_ due to FFI restriction at compile time. If we
proc, or any other which depends on FFI, the compiler will error with the try to use this proc, or any other which depends on FFI, the compiler will
message ``cannot evaluate`` and a dump of the macro's source code, along with a error with the message ``cannot evaluate`` and a dump of the macro's source
stack trace where the compiler reached before bailing out. We can get around code, along with a stack trace where the compiler reached before bailing out.
this limitation by using the ``slurp`` proc from the `system module We can get around this limitation by using the `slurp proc
<system.html>`_, which was precisely made for compilation time (just like <system.html#slurp>`_ from the `system module <system.html>`_, which was
``gorge`` which executes an external program and captures its output). 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`` 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. <tables.html#TTable>`_ object. Instead, it builds up Nim source code into
For each line of the configuration file a ``const`` variable will be generated. the ``source`` variable. For each line of the configuration file a ``const``
To avoid conflicts we prefix these variables with ``cfg``. In essence, what the variable will be generated (line 15). To avoid conflicts we prefix these
compiler is doing is replacing the line calling the macro with the following variables with ``cfg``. In essence, what the compiler is doing is replacing
snippet of code: the line calling the macro with the following snippet of code:
.. code-block:: nim .. code-block:: nim
const cfgversion= "1.1" const cfgversion= "1.1"
@ -873,13 +877,14 @@ snippet of code:
You can verify this yourself adding the line ``echo source`` somewhere at the 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 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 of calling the usual `quit proc <system.html#quit>`_ to abort (which we could
version calls the ``error`` proc. The ``error`` proc has the same behavior as still call) this version calls the `error proc <macros.html#error>`_ (line
``quit`` but will dump also the source and file line information where the 14). The ``error`` proc has the same behavior as ``quit`` but will dump also
error happened, making it easier for the programmer to find where compilation the source and file line information where the error happened, making it
failed. In this situation it would point to the line invoking the macro, but easier for the programmer to find where compilation failed. In this situation
**not** the line of ``data.cfg`` we are processing, that's something the macro it would point to the line invoking the macro, but **not** the line of
itself would need to control. ``data.cfg`` we are processing, that's something the macro itself would need
to control.
Generating AST by hand 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 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 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`` 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. macro <macros.html#dumpTree>`_, which is used as a statement macro instead of
Since we know that we want to generate a bunch of ``const`` symbols we can an expression macro. Since we know that we want to generate a bunch of
create the following source file and compile it to see what the compiler ``const`` symbols we can create the following source file and compile it to
*expects* from us: see what the compiler *expects* from us:
.. code-block:: nim .. code-block:: nim
import macros import macros
@ -937,6 +942,8 @@ with this knowledge, let's look at the finished version of the AST building
macro: macro:
.. code-block:: nim .. code-block:: nim
:number-lines:
import macros, strutils import macros, strutils
macro readCfgAndBuildAST(cfgFilename: string): stmt = 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`` 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 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 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``) For each input line we have to create a constant definition (``nnkConstDef``)
and wrap it inside a constant section (``nnkConstSection``). Once these and wrap it inside a constant section (``nnkConstSection``). Once these
variables are created, we fill them hierarchichally like the previous AST dump variables are created, we fill them hierarchichally (line 17) like the
tree showed: the constant definition is a child of the section definition, and previous AST dump tree showed: the constant definition is a child of the
the constant definition has an identifier node, an empty node (we let the section definition, and the constant definition has an identifier node, an
compiler figure out the type), and a string literal with the value. 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 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 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 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 generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this
treeRepr(result)`` you should get the same output as using the ``dumpTree`` example you add ``echo treeRepr(result)`` you should get the same output as
macro, but of course you can call that at any point of the macro where you using the ``dumpTree`` macro, but of course you can call that at any point of
might be having troubles. the macro where you might be having troubles.

View file

@ -39,7 +39,8 @@ type
meInvalidDirective, meInvalidDirective,
mwRedefinitionOfLabel, mwRedefinitionOfLabel,
mwUnknownSubstitution, mwUnknownSubstitution,
mwUnsupportedLanguage mwUnsupportedLanguage,
mwUnsupportedField
TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind, TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
arg: string) {.nimcall.} ## what to do in case of an error arg: string) {.nimcall.} ## what to do in case of an error
@ -55,7 +56,8 @@ const
meInvalidDirective: "invalid directive: '$1'", meInvalidDirective: "invalid directive: '$1'",
mwRedefinitionOfLabel: "redefinition of label '$1'", mwRedefinitionOfLabel: "redefinition of label '$1'",
mwUnknownSubstitution: "unknown substitution '$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 proc rstnodeToRefname*(n: PRstNode): string
@ -850,13 +852,13 @@ proc parseComment(p: var TRstParser): PRstNode =
type type
TDirKind = enum # must be ordered alphabetically! 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 dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
const const
DirIds: array[0..11, string] = ["", "author", "authors", "code-block", DirIds: array[0..12, string] = ["", "author", "authors", "code",
"container", "contents", "figure", "image", "include", "index", "raw", "code-block", "container", "contents", "figure", "image", "include",
"title"] "index", "raw", "title"]
proc getDirKind(s: string): TDirKind = proc getDirKind(s: string): TDirKind =
let i = find(DirIds, s) let i = find(DirIds, s)
@ -877,6 +879,9 @@ proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
proc parseSection(p: var TRstParser, result: PRstNode) 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) result = newRstNode(rnField)
var col = p.tok[p.idx].col var col = p.tok[p.idx].col
var fieldname = newRstNode(rnFieldName) var fieldname = newRstNode(rnFieldName)
@ -893,6 +898,10 @@ proc parseField(p: var TRstParser): PRstNode =
add(result, fieldbody) 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 result = nil
var atStart = p.idx == 0 and p.tok[0].symbol == ":" 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 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: else:
break 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 = proc getFieldValue(n: PRstNode, fieldname: string): string =
result = "" result = ""
if n.sons[1] == nil: return if n.sons[1] == nil: return
@ -1388,6 +1409,15 @@ type
TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.} 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) result = newRstNode(rnDirective)
var args: PRstNode = nil var args: PRstNode = nil
var options: PRstNode = nil var options: PRstNode = nil
@ -1421,6 +1451,9 @@ proc indFollows(p: TRstParser): bool =
proc parseDirective(p: var TRstParser, flags: TDirFlags, proc parseDirective(p: var TRstParser, flags: TDirFlags,
contentParser: TSectionParser): PRstNode = contentParser: TSectionParser): PRstNode =
## Returns a generic rnDirective tree.
##
## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
result = parseDirective(p, flags) result = parseDirective(p, flags)
if not isNil(contentParser) and indFollows(p): if not isNil(contentParser) and indFollows(p):
pushInd(p, p.tok[p.idx].ival) 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") # InternalError("Too many binary zeros in include file")
result = parseDoc(q) 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) result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
var filename = strip(getFieldValue(result, "file")) var filename = strip(getFieldValue(result, "file"))
if filename != "": if filename != "":
@ -1483,6 +1532,20 @@ proc dirCodeBlock(p: var TRstParser): PRstNode =
var n = newRstNode(rnLiteralBlock) var n = newRstNode(rnLiteralBlock)
add(n, newRstNode(rnLeaf, readFile(path))) add(n, newRstNode(rnLeaf, readFile(path)))
result.sons[2] = n 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 result.kind = rnCodeBlock
proc dirContainer(p: var TRstParser): PRstNode = proc dirContainer(p: var TRstParser): PRstNode =
@ -1566,7 +1629,8 @@ proc parseDotDot(p: var TRstParser): PRstNode =
result = dirRaw(p) result = dirRaw(p)
else: else:
rstMessage(p, meInvalidDirective, d) 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) of dkIndex: result = dirIndex(p)
else: rstMessage(p, meInvalidDirective, d) else: rstMessage(p, meInvalidDirective, d)
popInd(p) popInd(p)

View file

@ -24,7 +24,7 @@
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too. ## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
algorithm algorithm, parseutils
const const
HtmlExt = "html" HtmlExt = "html"
@ -63,6 +63,19 @@ type
PDoc = var TRstGenerator ## Alias to type less. 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, proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
config: StringTableRef, filename: string, config: StringTableRef, filename: string,
options: TRstParseOptions, options: TRstParseOptions,
@ -761,26 +774,102 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
height="17" hspace="2" vspace="2" />""", height="17" hspace="2" vspace="2" />""",
"\\includegraphics{$1}", [n.text]) "\\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) = 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 if n.sons[2] == nil: return
var params = d.parseCodeBlockParams(n)
var m = n.sons[2].sons[0] var m = n.sons[2].sons[0]
assert m.kind == rnLeaf 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", []) let (blockStart, blockEnd) = params.buildLinesHTMLTable(m.text)
if lang == langNone:
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr) 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) for letter in m.text: escChar(d.target, result, letter)
else: else:
var g: TGeneralTokenizer var g: TGeneralTokenizer
initGeneralTokenizer(g, m.text) initGeneralTokenizer(g, m.text)
while true: while true:
getNextToken(g, lang) getNextToken(g, params.lang)
case g.kind case g.kind
of gtEof: break of gtEof: break
of gtNone, gtWhitespace: of gtNone, gtWhitespace:
@ -790,7 +879,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
esc(d.target, substr(m.text, g.start, g.length+g.start-1)), esc(d.target, substr(m.text, g.start, g.length+g.start-1)),
tokenClassToStr[g.kind]]) tokenClassToStr[g.kind]])
deinitGeneralTokenizer(g) 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) = proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
var tmp = "" var tmp = ""

View file

@ -1102,11 +1102,11 @@ else:
const const
QuitSuccess* = 0 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. ## success.
QuitFailure* = 1 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. ## failure.
var programResult* {.exportc: "nim_program_result".}: int var programResult* {.exportc: "nim_program_result".}: int
@ -1119,10 +1119,11 @@ proc quit*(errorcode: int = QuitSuccess) {.
## Stops the program immediately with an exit code. ## Stops the program immediately with an exit code.
## ##
## Before stopping the program the "quit procedures" are called in the ## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with ``addQuitProc``. ``quit`` never ## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## returns and ignores any exception that may have been raised by the quit ## ``quit`` never returns and ignores any exception that may have been raised
## procedures. It does *not* call the garbage collector to free all the ## by the quit procedures. It does *not* call the garbage collector to free
## memory, unless a quit procedure calls ``GC_collect``. ## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_.
## ##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is ## 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 ## 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 ## 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) = template sysAssert(cond: bool, msg: string) =
when defined(useSysAssert): when defined(useSysAssert):
@ -1284,11 +1286,12 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.
proc addQuitProc*(QuitProc: proc() {.noconv.}) {. proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
importc: "atexit", header: "<stdlib.h>".} importc: "atexit", header: "<stdlib.h>".}
## adds/registers a quit procedure. Each call to ``addQuitProc`` ## Adds/registers a quit procedure.
## registers another quit procedure. Up to 30 procedures can be ##
## registered. They are executed on a last-in, first-out basis ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30
## (that is, the last function registered is the first to be executed). ## procedures can be registered. They are executed on a last-in, first-out
## ``addQuitProc`` raises an EOutOfIndex if ``quitProc`` cannot be ## basis (that is, the last function registered is the first to be executed).
## ``addQuitProc`` raises an EOutOfIndex exception if ``QuitProc`` cannot be
## registered. ## registered.
# Support for addQuitProc() is done by Ansi C's facilities here. # 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. ## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
## Opens a file named `filename` for reading. Then calls `readAll` ## Opens a file named `filename` for reading.
## and closes the file afterwards. Returns the string. ##
## Raises an IO exception in case of an error. ## 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.} proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.}
## Opens a file named `filename` for writing. Then writes the ## 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) spliceImpl(s, a, L, b)
proc slurp*(filename: string): string {.magic: "Slurp".} 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".} 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 ## .. code-block:: nim
## const myResource = staticRead"mydatafile.bin" ## const myResource = staticRead"mydatafile.bin"
## ##
## ``slurp`` is an alias for ``staticRead``. ## `slurp <#slurp>`_ is an alias for ``staticRead``.
proc gorge*(command: string, input = ""): string {. proc gorge*(command: string, input = ""): string {.
magic: "StaticExec".} = discard magic: "StaticExec".} = discard
## This is an alias for ``staticExec``. ## This is an alias for `staticExec <#staticExec>`_.
proc staticExec*(command: string, input = ""): string {. proc staticExec*(command: string, input = ""): string {.
magic: "StaticExec".} = discard magic: "StaticExec".} = discard
@ -2876,8 +2883,8 @@ proc staticExec*(command: string, input = ""): string {.
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v") ## "\nCompiled on " & staticExec("uname -v")
## ##
## ``gorge`` is an alias for ``staticExec``. Note that you can use this proc ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
## inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL ## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
## <nimc.html#passl-pragma>`_. ## <nimc.html#passl-pragma>`_.
proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.}