CSV2HTML in Nim

Created: by Pradeep Gowda Updated: Nov 04, 2023 Tagged: python · nim · utility

I implemented the csv2html idea in nimgithub repo. Nim is a strongly typed, compiles-to-c/c++/js-to-metal language that has advanced programming features, yet looks very friendly (Python-ish syntax).

Here is the core of the csv2html program in nim:

import parsecsv
import argparse
import os
import system


include "row.nimf"
include "table.nimf"
include "html.nimf"

proc csv2html(csvfile: string, delimiter: string, quotechar: string, caption: string, title: string, header: bool, cssurl: string) =
  var p: CsvParser
  var headerRow = ""
  p.open(csvfile)
  if header:
    p.readHeaderRow()
    headerRow = renderRow(p.headers, rowtype="th")
  var rows = newSeq[string](2)
  while p.readRow():
    rows.add(renderRow(p.row))
  p.close()
  echo renderHtml(renderTable(caption, headerRow, rows), title, cssurl)



when isMainModule:
  var p = newParser("CSV2HTML"):
    arg("csvfile")
    option("-d", "--delimiter", help="Field Delimiter. Default is ,.", default=",")
    option("-q", "--quotechar", help="Quote Character. Default is nothing")
    option("-t", "--title", help="Page Title. Will be printed in h1 tag")
    option("-c", "--caption", help="Table Caption")
    option("-s", "--css", help="Override CSS URL")
    flag("-f", "--header", help="Data has Header. First row will be rendered as `th`")
  if paramCount() < 1:
    echo p.help()
    quit(0)
  var args = p.parse()
  csv2html(args.csvfile, args.delimiter, args.quotechar, args.caption, ar

This looks very familiar to a Python programmer.

It is said that the language you write programs in, influences how you think. While nim is very close to Python syntax wise there are some standard library features of nim, that made me implement a feature differently.

In python, I wanted to keep to using just the standard library, and not pull in a templating language (I like Jinja2), to render the data into an HTML file. So, I ended up mixing the HTML template into the code. Which is fine for a small program like this.

In nim, the Source Code Filters library allows you to easily template things without having to reach for a “proper” templating language.

You might have wondered about lines starting with include above. I’m “including” the source code filter templates. For example, this is the code that renderes the csv data into an html table:

#? stdtmpl(subChar='$', metaChar='#')
#proc renderTable*(caption: string, headerRow: string, rows: seq[string]) : string =
#  result = ""
<table>
  #if caption != "":
    <caption>${caption}</caption>
  #end if
  ${headerRow}
  #for row in items(rows):
    $row
  #end for
</table>
#end proc

Its clear looking at the template, what’s happening. At the top, we are defining subChar=$, which means the strings with $ prefix will be replaced with the actual value. metaChar=# stands for the character that means something to the nim compiler. Essentially the lines starting with # will be treated as nim code.

The files includedd will be compiled at compile time into corresponding nim code. This allows us to write templating code without having to worry about escaping etc.

The project is defined as a nim project using the nimble package manager definition file:

# csv2html package
version = "0.1.0"
author = "Pradeep Gowda"
description = "render csv files as html files"
license = "BSD3"

# deps
requires "nim >= 1.0.0", "argparse >= 0.10.0"

srcDir = "src"
bin = @["csv2html"]

You can see that I’m defining the argparse third party dependency in the nimble file. Argparse is very much influenced by Pythons stdlib’s - argparse.

The project can be compiled into a binary file with: nimble build which will produce csv2html binary.

The idea of using source code filters came to me via this Nim forum thread – “AWK-style processing with Nim” mentioned by @deech.