1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
===================
Source Code Filters
===================
.. contents::
A `Source Code Filter (SCF)` transforms the input character stream to an in-memory
output stream before parsing. A filter can be used to provide templating
systems or preprocessors.
To use a filter for a source file the ``#?`` notation is used::
#? stdtmpl(subsChar = '$', metaChar = '#')
#proc generateXML(name, age: string): string =
# result = ""
<xml>
<name>$name</name>
<age>$age</age>
</xml>
As the example shows, passing arguments to a filter can be done
just like an ordinary procedure call with named or positional arguments. The
available parameters depend on the invoked filter. Before version 0.12.0 of
the language ``#!`` was used instead of ``#?``.
**Hint:** With ``--hint[codeBegin]:on`` or ``--verbosity:2``
(or higher) while compiling or `nim check`, Nim lists the processed code after
each filter application.
Usage
=====
First, put your SCF code in a separate file with filters specified in the first line.
**Note:** You can name your SCF file with any file extension you want, but the conventional extension is ``.tmpl``.
If we use `generateXML` code shown above and call the SCF file `xmlGen.tmpl`
In your `main.nim`:
.. code-block:: nim
include "xmlGen.tmpl"
echo generateXML("John Smith","42")
Pipe operator
=============
Filters can be combined with the ``|`` pipe operator::
#? strip(startswith="<") | stdtmpl
#proc generateXML(name, age: string): string =
# result = ""
<xml>
<name>$name</name>
<age>$age</age>
</xml>
Available filters
=================
Replace filter
--------------
The replace filter replaces substrings in each line.
Parameters and their defaults:
``sub: string = ""``
the substring that is searched for
``by: string = ""``
the string the substring is replaced with
Strip filter
------------
The strip filter simply removes leading and trailing whitespace from
each line.
Parameters and their defaults:
``startswith: string = ""``
strip only the lines that start with *startswith* (ignoring leading
whitespace). If empty every line is stripped.
``leading: bool = true``
strip leading whitespace
``trailing: bool = true``
strip trailing whitespace
StdTmpl filter
--------------
The stdtmpl filter provides a simple templating engine for Nim. The
filter uses a line based parser: Lines prefixed with a *meta character*
(default: ``#``) contain Nim code, other lines are verbatim. Because
indentation-based parsing is not suited for a templating engine, control flow
statements need ``end X`` delimiters.
Parameters and their defaults:
``metaChar: char = '#'``
prefix for a line that contains Nim code
``subsChar: char = '$'``
prefix for a Nim expression within a template line
``conc: string = " & "``
the operation for concatenation
``emit: string = "result.add"``
the operation to emit a string literal
``toString: string = "$"``
the operation that is applied to each expression
Example::
#? stdtmpl | standard
#proc generateHTMLPage(title, currentTab, content: string,
# tabs: openArray[string]): string =
# result = ""
<head><title>$title</title></head>
<body>
<div id="menu">
<ul>
#for tab in items(tabs):
#if currentTab == tab:
<li><a id="selected"
#else:
<li><a
#end if
href="${tab}.html">$tab</a></li>
#end for
</ul>
</div>
<div id="content">
$content
A dollar: $$.
</div>
</body>
The filter transforms this into:
.. code-block:: nim
proc generateHTMLPage(title, currentTab, content: string,
tabs: openArray[string]): string =
result = ""
result.add("<head><title>" & $(title) & "</title></head>\n" &
"<body>\n" &
" <div id=\"menu\">\n" &
" <ul>\n")
for tab in items(tabs):
if currentTab == tab:
result.add(" <li><a id=\"selected\" \n")
else:
result.add(" <li><a\n")
#end
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
#end
result.add(" </ul>\n" &
" </div>\n" &
" <div id=\"content\">\n" &
" " & $(content) & "\n" &
" A dollar: $.\n" &
" </div>\n" &
"</body>\n")
Each line that does not start with the meta character (ignoring leading
whitespace) is converted to a string literal that is added to ``result``.
The substitution character introduces a Nim expression *e* within the
string literal. *e* is converted to a string with the *toString* operation
which defaults to ``$``. For strong type checking, set ``toString`` to the
empty string. *e* must match this PEG pattern::
e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
x <- '{' x+ '}' / [^}]*
To produce a single substitution character it has to be doubled: ``$$``
produces ``$``.
The template engine is quite flexible. It is easy to produce a procedure that
writes the template code directly to a file::
#? stdtmpl(emit="f.write") | standard
#proc writeHTMLPage(f: File, title, currentTab, content: string,
# tabs: openArray[string]) =
<head><title>$title</title></head>
<body>
<div id="menu">
<ul>
#for tab in items(tabs):
#if currentTab == tab:
<li><a id="selected"
#else:
<li><a
#end if
href="${tab}.html" title = "$title - $tab">$tab</a></li>
#end for
</ul>
</div>
<div id="content">
$content
A dollar: $$.
</div>
</body>
|