mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 11:54:11 +00:00
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:
@@ -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.} =
|
||||
|
||||
@@ -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",
|
||||
|
||||
98
doc/tut2.txt
98
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.
|
||||
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 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).
|
||||
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``
|
||||
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 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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.}
|
||||
|
||||
Reference in New Issue
Block a user