So originally I wanted to write a up a nice example to do the replacements via
the scanf macro:
[https://nim-lang.github.io/Nim/strscans.html](https://nim-lang.github.io/Nim/strscans.html)
by defining tuples of strings to match against and their replacements, but I
hit a dead end, because an element of a const tuple doesn't count as a static
string for the pattern.
Also scanf turned out to be more problematic than I thought, because the $*
term does not like to match any string until the end.
But since your book is (at least partly) about Nim macros and writing macros is
fun, I built the following even longer version of your code, haha. It also
includes a custom matcher that matches anything until the end of the string.
# File: web.nim
import strutils, os, strscans, macros
let input = open("rage.md")
let form = """
"""
echo "Content-type: text/html\n\n"
echo """
"""
echo ""
proc rest(input: string; match: var string, start: int): int =
## matches until the end of the string
match = input[start .. input.high]
# result is either 1 (string is empty) or the number of found chars
result = max(1, input.len - start)
macro match(args, line: typed): untyped =
## match the `args` via `scanf` in `line`. `args` must be a `[]` of
## `(scanf string matcher, replacement string)` tuples, where the latter
## has to include a single `$#` to indicate the position of the
replacement.
## The order of the `args` is important, since an if statement is built.
let argImpl = args.getImpl
expectKind argImpl, nnkBracket
result = newStmtList()
let matched = genSym(nskVar, "matched")
result.add quote do:
var `matched`: string
var ifStmt = nnkIfStmt.newTree()
for el in argImpl:
expectKind el, nnkTupleConstr
let toMatch = el[0]
let toReplace = el[1]
let ifBody = nnkStmtList.newTree(nnkCall.newTree(ident"echo",
nnkCall.newTree(ident"%",
toReplace,
matched)),
nnkAsgn.newTree(matched, newLit("")))
let ifCond = nnkCall.newTree(ident"scanf", line, toMatch, matched)
ifStmt.add nnkElifBranch.newTree(ifCond, ifBody)
result.add ifStmt
echo result.repr
const h1title = ("# ${rest}", "$#")
const h2title = ("## ${rest}", "$#")
const elseLine = ("${rest}", "$#")
const replacements = [h1title, h2title, elseLine]
for line in input.lines:
match(replacements, line)
# produces:
# var matched: string
# if scanf("# ${rest}", line, matched):
# echo h1title[1] % matched
# if scanf("## ${rest}", line, matched):
# echo h2title[1] % matched
# if scanf("${rest}", line, matched):
# echo elseLine[1] % matched
echo form
let qs = getEnv("QUERY_STRING", "none").split({'+'}).join(" ")
if qs != "none" and qs.len > 0:
let output = open("visitors.txt", fmAppend)
write(output, qs&"\n")
output.close
let inputVisitors= open("visitors.txt")
for line in inputVisitors.lines:
match(replacements, line)
inputVisitors.close
echo ""
input.close
Run
This is totally not practicle I'd say and one's better off writing something by
hand or using the excellent
[https://github.com/zevv/npeg](https://github.com/zevv/npeg) by @zevv.
Still fun though. And if someone wants to improve on this...
Finally, to just remove a prefix of a string, you may just use removePrefix
from strutils:
[https://nim-lang.github.io/Nim/strutils.html#removePrefix%2Cstring%2Cstring](https://nim-lang.github.io/Nim/strutils.html#removePrefix%2Cstring%2Cstring)
Note that it only works inplace on string. You could use the new outplace
though:
[https://github.com/nim-lang/Nim/pull/12599](https://github.com/nim-lang/Nim/pull/12599)