4223
|
1 *** THE PLOKI LANGUAGE: EXPRESSIONS ***
|
|
2
|
|
3
|
|
4 GENERAL
|
|
5
|
|
6 ploki is an imperative, unstructured language, similar to BASIC. Some of its
|
|
7 features are borrowed from Perl. ploki's data structures are fully dynamic,
|
|
8 i.e. they resize themselves when necessary. Strings know their length and
|
|
9 may contain binary data.
|
|
10
|
|
11
|
|
12 VALUES
|
|
13
|
|
14 There are six types of values:
|
|
15 1. String: A (possibly empty) sequence of characters.
|
|
16 Any value can be converted to a string.
|
|
17 2. Number: A (double precision) floating point number.
|
|
18 Any value can be converted to a string.
|
|
19 3. IO handle: Can be read from or written to.
|
|
20 4. List: A (possibly empty) sequence of values.
|
|
21 5. Bound expression: See @OMFG below.
|
|
22 6. Undefined.
|
|
23
|
|
24 Values aren't necessarily one thing or another. There's no place to declare
|
|
25 a variable to be of type "string", type "number", or anything else. In fact,
|
|
26 you can't declare variables at all. Every variable you use is initialized
|
|
27 with the undefined value ("undef" for short).
|
|
28
|
|
29
|
|
30 Truth
|
|
31
|
|
32 0, "0", "", #<#> and undef are false. Any other value is true.
|
|
33
|
|
34
|
|
35 Conversions
|
|
36
|
|
37 Conversions happen as follows:
|
|
38 * number -> string: decimal floating point, the "normal" way
|
|
39 * IO handle -> string: filename
|
|
40 * list -> string: concatenation of the stringified list elements
|
|
41 * bound expression -> string: undefined results
|
|
42 * undef -> string: "" (the empty string)
|
|
43
|
|
44 * string -> number: initial whitespace is skipped, the rest of the string
|
|
45 is interpreted as a decimal floating point number
|
|
46 (as done by strtod())
|
|
47 * IO handle -> number: undefined results
|
|
48 * list -> number: sum of the numified list elements
|
|
49 * bound expression -> number: undefined results
|
|
50 * undef -> number: 0.0
|
|
51
|
|
52
|
|
53 Constructors
|
|
54
|
|
55 Anything starting with a digit or a period followed by a digit is taken to
|
|
56 be the beginning of a number (again, as parsed by strtod(), which means that
|
|
57 scientific notation (e.g. 1e-23) is allowed). String literals start with '"'
|
|
58 and end with '"' or end-of-line. The following escape sequences are
|
|
59 recognized:
|
|
60
|
|
61 \OOO octal char (where OOO matches [0-7]{1,3})
|
|
62 \xXX hex char (where XX matches [0-9a-fA-F]{1,2})
|
|
63 \a alarm
|
|
64 \b backspace
|
|
65 \f form feed
|
|
66 \n newline
|
|
67 \r carriage return
|
|
68 \t tab
|
|
69 \v vertical tab
|
|
70
|
|
71 \cX the character resulting from (toupper(X) + 64) % 128
|
|
72 (this depends on the character set used)
|
|
73
|
|
74 \V<value>
|
|
75 The stringified result of evaluating <value>. In other words, this
|
|
76 can be used to interpolate expressions in strings.
|
|
77 Example: "2 + 2 = \V(2 + 2)" yields "2 + 2 = 4".
|
|
78
|
|
79
|
|
80 Lists start with #< and end with #> or end-of-line. Example: #<2 +1 (3 +4)#>
|
|
81 is equivalent to #<2 1 7#>.
|
|
82
|
|
83 The empty value yields undef: `-2' is parsed as ` - 2', undef is converted
|
|
84 to 0.0, and the result is 0.0 - 2.0, which is -2.
|
|
85
|
|
86
|
|
87 Special symbols
|
|
88
|
|
89 There are the following special symbols, similar to Perl's punctuation
|
|
90 variables:
|
|
91
|
|
92 * \N (where N is a non-empty sequence of digits)
|
|
93 Contains the substring that was matched by the Nth capturing group in
|
|
94 the previous pattern match.
|
|
95 * \!
|
|
96 If used numerically, yields the current value of C's errno variable, or
|
|
97 in other words, if a system or library call fails, it sets this
|
|
98 variable. If used as a string, yields the corresponding system error
|
|
99 string (i.e. strerror(errno)).
|
|
100 * \?
|
|
101 Yields a random number in the range [0,1).
|
|
102 * \_
|
|
103 Contains the return value of the last command.
|
|
104 * \@
|
|
105 The argument the current function was called with.
|
|
106 * \ARG
|
|
107 Number of command line options (C's argc).
|
|
108 * \AUSG
|
|
109 The standard output IO handle (open mode: "WF").
|
|
110 * \EING
|
|
111 The standard input IO handle (open mode: "RZ").
|
|
112 * \E
|
|
113 Euler's number: 2.718281828...
|
|
114 * \FEHL
|
|
115 The standard error IO handle (open mode: "WF").
|
|
116 * \PI
|
|
117 pi: 3.14159265...
|
|
118 * \
|
|
119 undef
|
|
120
|
|
121
|
|
122 VARIABLES
|
|
123
|
|
124 There are two kinds of variables: plain variables and subscripted variables.
|
|
125 A plain variable has the form [A-Za-z$]+, i.e. one or more alphabetic
|
|
126 characters (where '$' is considered alphabetic). A subscripted variable
|
|
127 looks like a plain variable immediately followed by an opening paren "(", an
|
|
128 expression (the result of which is converted to a string) and possibly a
|
|
129 closing paren ")".
|
|
130
|
|
131
|
|
132 UNARY OPERATORS
|
|
133
|
|
134 Each unary operator takes a value and returns a value. The only exception is
|
|
135 @OMFG, which is more a constructor than an operator.
|
|
136
|
|
137 * \ARG:
|
|
138 Takes an integer and returns the corresponding command line argument
|
|
139 (like C's argv[]).
|
|
140 * \L
|
|
141 Returns a lowercased version of its operand (like Perl's lc).
|
|
142 * \Q
|
|
143 Returns its operand with all non-word characters backslashed, i.e. any
|
|
144 character not matching [a-zA-Z0-9_] will be preceded by a \ in the
|
|
145 returned string (like Perl's quotemeta).
|
|
146 * \R
|
|
147 Returns its operand with all non-word characters regex-escaped, i.e. any
|
|
148 character not matching [a-zA-Z0-9_] will be followed by a ! in the
|
|
149 returned string (like s/(\W)/$1!/g in Perl).
|
|
150 * \U
|
|
151 Returns an uppercased version of its operand (like Perl's uc).
|
|
152 * @+
|
|
153 For an operand N, returns the offset of the character after the portion
|
|
154 of the string matched by the Nth capturing group in the last pattern
|
|
155 matching.
|
|
156 * @-
|
|
157 For an operand N, returns the offset of the portion of the string
|
|
158 matched by the Nth capturing group in the last pattern matching.
|
|
159 * @ABS
|
|
160 Returns the absolute value of its operand.
|
|
161 * @ACOS
|
|
162 Returns the arc cosine of its operand.
|
|
163 * @APERS
|
|
164 Takes a list of two elements, a filename and a mode string. Opens the
|
|
165 specified file and returns an IO handle or undef on error. If the mode
|
|
166 string contains "A", the file is created if necessary and opened for
|
|
167 appending (i.e. every write goes to the end of the file); if the mode
|
|
168 string contains "W", the file is truncated/created and opened for
|
|
169 writing; otherwise the file is opened for reading, and the open fails if
|
|
170 the file doesn't exist. If the mode string contains "+", reading is
|
|
171 added for "A" and "W", and writing otherwise. "R+" is almost always
|
|
172 preferred for read/write access; "W+" would clobber the file first. The
|
|
173 resulting IO handle defaults to text mode; add "B" to open the file in
|
|
174 binary mode. Adding "F" turns on autoflush mode, i.e. the stream is
|
|
175 flushed after every write. If the file is opened for reading and the
|
|
176 mode string contains "Z", the resulting stream can be used with the
|
|
177 regex match operator (~).
|
|
178 * @ASIN
|
|
179 Returns the arc sine of its operand.
|
|
180 * @ATAN
|
|
181 Returns the arc tangent of its operand.
|
|
182 * @ATAN2
|
|
183 Takes a list of two elements #<y x#> and returns the arc tangent of x
|
|
184 and y. It is similar to @ATAN (y / x), except that the signs of both
|
|
185 arguments are used to determine the quadrant of the result.
|
|
186 * @CHR
|
|
187 Takes an integer and returns the character represented by that number in
|
|
188 the character set.
|
|
189 * @COS
|
|
190 Returns the cosine of its operand.
|
|
191 * @DEF-P
|
|
192 Returns "" if its operand is undef, 1 otherwise.
|
|
193 * @EDD-P
|
|
194 Returns 1 if the end-of-file indicator for its operand (which must be an
|
|
195 IO handle) is set, "" otherwise.
|
|
196 * @ENV or \ENV
|
|
197 Returns the value of the specified environment variable.
|
|
198 * @ERR-P
|
|
199 Returns 1 if the error indicator for its operand (which must be an IO
|
|
200 handle) is set, "" otherwise.
|
|
201 * @EVAL
|
|
202 Evaluates and returns its operand. If an exception is thrown during the
|
|
203 evaluation of its operand, assigns the exception value to \_ and returns
|
|
204 undef.
|
|
205 * @GET
|
|
206 Takes an IO handle, reads one character, and returns its numeric value or
|
|
207 -1 on end-of-file or error.
|
|
208 * @INT
|
|
209 Returns the integer portion of its operand.
|
|
210 * @IO-P
|
|
211 Returns 1 if its operand is an IO handle, "" otherwise.
|
|
212 * @LAPERS
|
|
213 Takes a filename, opens it for reading, and returns the resulting IO
|
|
214 handle or undef on failure. Equivalent to @APERS #<filename "RB"#>.
|
|
215 * @LEGS
|
|
216 Takes an IO handle, reads a line and returns it. Returns "" on
|
|
217 end-of-file and undef on error.
|
|
218 * @LENGTH
|
|
219 If passed a list, returns the number of elements; otherwise, returns the
|
|
220 length of its operand in characters.
|
|
221 * @LG
|
|
222 Returns the base-10 logarithm of its operand.
|
|
223 * @LN
|
|
224 Returns the natural logarithm (base e) of its operand.
|
|
225 * @NEG
|
|
226 Performs arithmetic negation.
|
|
227 * @NOT
|
|
228 Returns 1 if its operand is false, "" otherwise.
|
|
229 * @NUM
|
|
230 Converts its operand to a number.
|
|
231 * @ORD
|
|
232 Returns the numeric value of the first character of its operand.
|
|
233 * @OMFG
|
|
234 Does not evaluate its operand but wraps it up in a bound expression by
|
|
235 replacing all variables by their current value.
|
|
236 * @REMOVE
|
|
237 Tries to remove the specified file. Returns 1 for success, "" for
|
|
238 failure.
|
|
239 * @RENAEM
|
|
240 Takes a list of two filenames #<OLD NEW#> and tries to rename OLD to
|
|
241 NEW. Returns 1 for success, "" for failure.
|
|
242 * @REVERSE
|
|
243 If passed a list, returns a list consisting of the elements of the
|
|
244 original list in the opposite order. Otherwise, returns a string with
|
|
245 all the characters in the opposite order.
|
|
246 * @SAG
|
|
247 Returns a value representing the current file position of its operand
|
|
248 (which must be an IO handle) suitable as the second argument of @SUCH.
|
|
249 If the file was opened in binary mode, this is the number of bytes from
|
|
250 the beginning of the file.
|
|
251 * @SAPERS
|
|
252 Takes a filename, opens it for writing, and returns the resulting IO
|
|
253 handle or undef on failure. The file is created if it doesn't exist and
|
|
254 truncated to length 0 otherwise. Equivalent to @APERS #<filename
|
|
255 "WBF"#>.
|
|
256 * @SIN
|
|
257 Returns the sine of its operand.
|
|
258 * @SQRT
|
|
259 Returns the square root of its operand.
|
|
260 * @STR
|
|
261 Converts its operand to a string.
|
|
262 * @SUCH
|
|
263 Takes a list of two or three elements, #<IO POS WHENCE#>. WHENCE
|
|
264 defaults to 0 if omitted. Sets the current file position of IO (which
|
|
265 must be an IO handle) to POS (relative to WHENCE). WHENCE must be one of
|
|
266 0 (beginning of the file), 1 (current position) or 2 (end of file). If
|
|
267 IO is in text mode, POS must be a value returned by @SAG and WHENCE must
|
|
268 be 0, or POS must be 0. If IO is in binary mode, the new position
|
|
269 (measured in bytes from the beginning of the file) is obtained by adding
|
|
270 POS to the position specified by WHENCE. In this case POS=0 and WHENCE=2
|
|
271 may not work, depending on your C library.
|
|
272 * @TAN
|
|
273 Returns the tangent of its operand.
|
|
274 * @TYPE OF
|
|
275 Returns a string describing the type of its operand: "string" for
|
|
276 strings, "number" for numbers, "stream" for IO handles, "list" for
|
|
277 lists, "stuff" for bound expressions, "nothing" for undef.
|
|
278 * @<label> (where <label> is a static label)
|
|
279 Calls the static label <label> with its operand.
|
|
280 * @
|
|
281 Calls the dynamic label specified by its operand.
|
|
282
|
|
283
|
|
284 BINARY OPERATORS
|
|
285
|
|
286 Some of the operators (marked with (SL)) below double as string and list
|
|
287 operators. They do the same thing with strings and lists by treating strings
|
|
288 as lists of characters. Boolean and comparison operators return 1 for true
|
|
289 and "" for false.
|
|
290
|
|
291 _ (SL) If given two lists, it concatenates them. Otherwise, it converts
|
|
292 its operands to strings and concatenates them.
|
|
293
|
|
294 + Adds two numbers.
|
|
295 - Subtracts two numbers.
|
|
296 * Multiplies two numbers.
|
|
297 / Divides two numbers.
|
|
298 % Returns the modulus (remainder from division) of two numbers.
|
|
299 ^ Exponentiation.
|
|
300
|
|
301 [ (SL) Given a string/list B and an integer N, returns the
|
|
302 substring/sublist of B starting at index N. Negative indices start from
|
|
303 the end of the string/list.
|
|
304 Example: "foo" [ 1 yields "oo"; "foobar" [ -2 yields "ar".
|
|
305
|
|
306 ] (SL) Given a string/list B and an integer N, returns the
|
|
307 substring/sublist of B having a length of N. Negative lengths start
|
|
308 from the end of the string/list.
|
|
309 Example: "foo" ] 1 yields "f"; "foobar" ] -2 yields "foob"
|
|
310
|
|
311 < Numeric less than.
|
|
312 > Numeric greater than.
|
|
313 { (SL) String/list less than.
|
|
314 } (SL) String/list greater than.
|
|
315 = Numeric equality.
|
|
316 ! Numeric inequality.
|
|
317 : (SL) String/list equality.
|
|
318 ; (SL) String/list inequality.
|
|
319
|
|
320 & Logical and.
|
|
321 | Logical or.
|
|
322
|
|
323 ~ Pattern match. The left operand is the string or IO stream to be
|
|
324 matched, the right operand is a string forming a regular expression
|
|
325 (see PATTERNS below). On failure, "" is returned. On success, returns
|
|
326 the position of the match and sets \_ to the position after the match.
|
|
327
|
|
328 ?o~ Similar to ~ above, except that the right operand is only evaluated the
|
|
329 first time it is executed. This means that (foo ?o~ bar) won't see any
|
|
330 changes to bar after the first match attempt (like /o in Perl).
|
|
331
|
|
332 ` Given a number X and an integer N (which must be in the range 2 .. 36),
|
|
333 returns a string representation of X in base N.
|
|
334 ' Given a string S and an integer N (which must be in the range 2 .. 36),
|
|
335 interprets S as a number in base N and returns the result.
|
|
336
|
|
337 (the empty operator) or
|
|
338 . (SL) This one is really three different operators, depending on the
|
|
339 type of its left operand:
|
|
340 - IO handle: Attempts to read and return N bytes where N is the right
|
|
341 operand.
|
|
342 - Bound expression: Evaluates the bound expression with \@ temporarily
|
|
343 set to the right operand.
|
|
344 - String/list: Returns the element at the index specified by the right
|
|
345 operand.
|
|
346
|
|
347 , Returns its right operand.
|
|
348
|
|
349
|
|
350 PATTERNS
|
|
351
|
|
352 The ~ operator tries to match a string or IO handle against a pattern. A
|
|
353 match succeeds if the pattern matches a substring of the matched string.
|
|
354 When matching an IO handle (which must have been opened with "Z"), the
|
|
355 matched characters are removed from the input stream if the match succeeds.
|
|
356
|
|
357 Whitespace in the pattern is mostly ignored (that is, the pattern " a b" is
|
|
358 equivalent to "ab"). Exceptions are marked [S] below. There is a special
|
|
359 kind of "whitespace": everything between "[" and "#]" is ignored (except for
|
|
360 "!"s), so you can write "foo [ a comment with [! in it #] bar" instead of
|
|
361 "foobar".
|
|
362
|
|
363 A search pattern consists of one or more branches, separated by "|". It
|
|
364 succeeds if one of its branches succeeds. The branches are tried in the
|
|
365 order they're specified.
|
|
366
|
|
367 A branch is a (possibly empty) sequence of pieces. It succeeds if all of the
|
|
368 pieces match in turn.
|
|
369
|
|
370 A piece is an atom, possibly followed by a quantifier. A missing quantifier
|
|
371 means the atom must match exactly once.
|
|
372
|
|
373 A quantifier is one of the following (N, M are nonnegative integers):
|
|
374 * 0 or more of the preceding atom (greedy)
|
|
375 *? 0 or more of the preceding atom (nongreedy) [S]
|
|
376 + 1 or more of the preceding atom (greedy)
|
|
377 +? 1 or more of the preceding atom (nongreedy) [S]
|
|
378 ? 0 or 1 of the preceding atom (greedy)
|
|
379 ?? 0 or 1 of the preceding atom (nongreedy) [S]
|
|
380 :N: exactly N of the preceding atom [S]
|
|
381 :N:? exactly N of the preceding atom [S]
|
|
382 :N,: N or more of the preceding atom (greedy) [S]
|
|
383 :N,:? N or more of the preceding atom (nongreedy) [S]
|
|
384 :N,M: at least N but not more than M of the preceding atom (greedy)
|
|
385 [S]
|
|
386 :N,M:? at least N but not more than M of the preceding atom (nongreedy)
|
|
387 [S]
|
|
388
|
|
389 An atom is either a character class, an assertion, a selection, a subgroup,
|
|
390 a capturing group, a backreference, an independent subgroup, an
|
|
391 abbreviation, an escaped character, or an ordinary character.
|
|
392
|
|
393 A character class is either a built-in class or a custom class. A built-in
|
|
394 class is one of the following:
|
|
395 . a character
|
|
396 c! a control character
|
|
397 C! a non-control character
|
|
398 d! a digit
|
|
399 D! a non-digit
|
|
400 l! a lowercase character
|
|
401 L! a non-lowercase character
|
|
402 p! a printable character
|
|
403 P! a non-printable character
|
|
404 q! an alphanumeric character
|
|
405 Q! a non-alphanumeric character
|
|
406 s! a space character
|
|
407 S! a non-space character
|
|
408 u! an uppercase character
|
|
409 U! a non-uppercase character
|
|
410 w! a word character, i.e. one of a-zA-Z0-9_
|
|
411 W! a non-word character
|
|
412 x! a hex digit, i.e. one of 0-9a-fA-F
|
|
413 X! a non-hex character
|
|
414
|
|
415 [S] A custom class is a (possibly empty) sequence of characters, enclosed in
|
|
416 '' (single quotes). It matches one of the listed characters, unless the
|
|
417 first character is '^', in which case it matches one of the not listed
|
|
418 characters. Whitespace is significant inside character classes. A range of
|
|
419 characters can be specified by putting a '-' between the endpoints. In
|
|
420 addition, all built-in classes listed above (except for .) and the following
|
|
421 subclasses are available:
|
|
422
|
|
423 [:alnum:] equivalent to q!
|
|
424 [:^alnum:] equivalent to Q!
|
|
425 [:alpha:] equivalent to a!
|
|
426 [:^alpha:] equivalent to A!
|
|
427 [:cntrl:] equivalent to c!
|
|
428 [:^cntrl:] equivalent to C!
|
|
429 [:digit:] equivalent to d!
|
|
430 [:^digit:] equivalent to D!
|
|
431 [:graph:] equivalent to '^P! ', i.e. any printable character except space
|
|
432 [:^graph:] equivalent to 'P! ', i.e. any non-printable character or space
|
|
433 [:lower:] equivalent to l!
|
|
434 [:^lower:] equivalent to L!
|
|
435 [:print:] equivalent to p!
|
|
436 [:^print:] equivalent to P!
|
|
437 [:punct:] equivalent to '^P!q! ', i.e. any non-alphanumeric, non-space
|
|
438 printable character
|
|
439 [:^punct:] equivalent to 'P!q! ', i.e. a non-printable character, an
|
|
440 alphanumeric character or space
|
|
441 [:space:] equivalent to s!
|
|
442 [:^space:] equivalent to S!
|
|
443 [:upper:] equivalent to u!
|
|
444 [:^upper:] equivalent to U!
|
|
445 [:xdigit:] equivalent to x!
|
|
446 [:^xdigit:] equivalent to X!
|
|
447
|
|
448 To include a literal ^, !, - or ' in a character class, escape it with a
|
|
449 following !. Example: '!!'!' is a class that matches ! or '.
|
|
450
|
|
451 An assertion is something that doesn't consume parts of the matched string,
|
|
452 i.e. it matches with zero length. There are built-in assertions and custom
|
|
453 assertions. A built-in assertion is one of the following:
|
|
454
|
|
455 ^ at beginning of string
|
|
456 $ at end of string
|
|
457 A! at beginning of line
|
|
458 Z! at end of line
|
|
459 b! at a word boundary
|
|
460 B! not at a word boundary
|
|
461
|
|
462 A custom assertion is either a positive assertion, a negative assertion or a
|
|
463 backreference check. A positive assertion consists of "[", followed by a
|
|
464 pattern, followed by "&]" [S]. It succeeds (without consuming parts of the
|
|
465 matched string) if the enclosed pattern succeeds. A negative assertion
|
|
466 consists of "[", followed by a pattern, followed by "^]" [S]. It succeeds if
|
|
467 the enclosed pattern fails. A backreference check consists of "[", followed
|
|
468 by a number, followed by "]" [S]. It succeeds if the corresponding capturing
|
|
469 group succeeded.
|
|
470
|
|
471 A selection consists of "[", optionally followed by a pattern followed by
|
|
472 "|", followed by a branch, followed by an atom, followed by "?]" [S]. It
|
|
473 matches as follows: First the atom is tried. If it matches, the preceding
|
|
474 branch is tried; otherwise matching continues with the preceding pattern if
|
|
475 specified. In other words, it looks like
|
|
476 "[no-pattern|yes-pattern(conditition)?]" or "[yes-pattern(condition)?]".
|
|
477 Example: "{(!}?'^()'+[)![0]?]" matches a chunk of non-parentheses, possibly
|
|
478 included in parentheses themselves.
|
|
479
|
|
480 A subgroup is a parenthesized pattern. It succeeds if the enclosed pattern
|
|
481 succeeds.
|
|
482
|
|
483 A capturing group is a pattern surrounded by "{" and "}". It succeeds if the
|
|
484 enclosed pattern succeeds. As a side effect, it sets \N, @-(N) and @+(N)
|
|
485 where N is the number of the capturing group. Capturing groups are numbered
|
|
486 from the right, counting the closing braces (starting with 0).
|
|
487
|
|
488 A backreference is N! where N is a non-negative integer. It succeeds if it
|
|
489 can match the same substring that was matched by the Nth capturing group.
|
|
490
|
|
491 An independent subgroup is a pattern, surrounded by "<" and ">". It succeeds
|
|
492 if the enclosed pattern succeeds, but when it does, it won't backtrack in
|
|
493 the enclosed pattern.
|
|
494
|
|
495 An abbreviation is a (possibly empty) sequence of characters surrounded by
|
|
496 "`" and "`". All metacharacters lose their special meaning inside an
|
|
497 abbreviation, except for ` (which marks the end of the abbreviation) and !
|
|
498 (which escapes the previous character, as usual). `abcd` is equivalent to
|
|
499 (a(b(cd?)?)?)?, i.e. an abbreviation for x matches the longest possible
|
|
500 initial substring of x.
|
|
501
|
|
502 [S] An escaped character is a special character like "!" or "+" (or
|
|
503 whitespace), followed by an "!". It matches that character literally.
|
|
504
|
|
505 An ordinary character matches itself.
|
|
506
|
|
507
|
|
508 # vi: set tw=76 et:
|