---
author: ''
category: ''
date: 2018-04-30 14:44:00 UTC
description: ''
link: ''
priority: ''
slug: playing-with-nim
tags: 'programming, python, nim '
title: Playing with Nim
type: text
updated: 2018-04-30 14:44:00 UTC
url_type: ''
---
A few days ago I saw a mention in twitter about a language called `Nim `__
.. raw:: html
Se ve interesante ~90% Python-like, compila a Binario o JS, no dependencias ni runtime.https://t.co/JF3vIScWqG
And... why not. I am a bit stale in my programming language variety. I used to be fluent in a dozen, now I do 80% python 10% go, some JS and almost nothing else.
Because I learn by doing, I decided to do something. Because I did not want a problem I did not know how to solve to get in the way of the language, I decided to reimplement the example for the `python book `__ I am writing: a text layout engine that outputs SVG, based on harfbuzz, freetype2 and other things.
This is a good learning project **for me**, because a lot of my coding is glueing things together, I hardly ever do things `from scratch `__.
So, I decided to start in somewhat random order.
Preparation
-----------
.. role:: nimrod(code)
:language: nimrod
I read the `Nim Tutorial `__ quickly. I ended referring to it and to `Nim by example `__ a lot. While trying out a new language one is bound to forget syntax. It happens.
Wrote a few "hello world" 5 line programs to see that the ecosystem was installed correctly. Impression: builds are fast-ish. THey can get actually fast if you start using tcc instead of gcc.
SVG Output
----------
I looked for libraries that were the equivalent of `svgwrite, `__ which I am using on the python side. Sadly, such a thing doesn't seem to exist for nim.
So, I wrote my own. It's very rudimentary, and surely the nim code is garbage for experienced nim coders, but I ended using the xmltree module of nim's standard library and everything!
.. code:: nimrod
import xmltree
import strtabs
import strformat
type
Drawing* = tuple[fname: string, document: XmlNode]
proc NewDrawing*(fname: string, height:string="100", width:string="100"): Drawing =
result = (
fname: fname,
document: <>svg()
)
var attrs = newStringTable()
attrs["baseProfile"] = "full"
attrs["version"] = "1.1"
attrs["xmlns"] = "http://www.w3.org/2000/svg"
attrs["xmlns:ev"] = "http://www.w3.org/2001/xml-events"
attrs["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
attrs["height"] = height
attrs["width"] = width
result.document.attrs = attrs
proc Add*(d: Drawing, node: XmlNode): void =
d.document.add(node)
proc Rect*(x: string, y: string, width: string, height: string, fill: string="blue"): XmlNode =
result = <>rect(
x=x,
y=y,
width=width,
height=height,
fill=fill
)
proc Text*(text: string, x: string, y: string, font_size: string, font_family: string="Arial"): XmlNode =
result = <>text(newText(text))
var attrs = newStringTable()
attrs["x"] = x
attrs["y"] = y
attrs["font-size"] = font_size
attrs["font-family"] = font_family
result.attrs = attrs
proc Save*(d:Drawing): void =
writeFile(d.fname,xmlHeader & $(d.document))
when isMainModule:
var d = NewDrawing("foo.svg", width=fmt"{width}cm", height=fmt"{height}cm")
d.Add(Rect("10cm","10cm","15cm","15cm","white"))
d.Add(Text("HOLA","12cm","12cm","2cm"))
d.Save()
While writing this I ran into a few issues abd saw a few nice things:
To build a :nimrod:`svg` tag, you can use :nimrod:`<>svg(attr=value)` which is delightful syntax. But what happens if the attr is :nimrod:`"xmlns:ev"`? That is not a valid identifier, so it doesn't work.
So I worked around it by creating a :nimrod:`StringTable` filling it and setting all attributes at once.
A good thing is the :nimrod:`when` keyword. usingit as :nimrod:`when isMainModule` means that code is built and executed when :nimrod:`svgwrite.nim` is built standalone, and not when used as a module.
Another good thing is the syntax sugar for what in python we would call "object's methods".
Because :nimrod:`Add` takes a :nimrod:`Drawing` as first argument, you can just call :nimrod:`d.Add()` if :nimrod:`d` is a :nimrod:`Drawing`. Is simple, it's clear and it's useful and I like it.
One bad thing is that sometimes importing a module will cause weird errors that are hard to guess. For example, this simplified version fails to build:
.. code:: nimrod
import xmltree
type
Drawing* = tuple[fname: string, document: XmlNode]
proc NewDrawing*(fname: string, height:string="100", width:string="100"): Drawing =
result = (
fname: fname,
document: <>svg(width=width, height=height)
)
when isMainModule:
var d = NewDrawing("foo.svg")
::
$ nim c svg1.nim
Hint: used config file '/etc/nim.cfg' [Conf]
Hint: system [Processing]
Hint: svg1 [Processing]
Hint: xmltree [Processing]
Hint: macros [Processing]
Hint: strtabs [Processing]
Hint: hashes [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: math [Processing]
Hint: algorithm [Processing]
Hint: os [Processing]
Hint: times [Processing]
Hint: posix [Processing]
Hint: ospaths [Processing]
svg1.nim(9, 19) template/generic instantiation from here
lib/nim/core/macros.nim(556, 26) Error: undeclared identifier: 'newStringTable'
WAT? I am not using `newStringTable` anywhere! The solution is to add :nimrod:`import strtabs` which
defines it, but there is really no way to guess which imports will trigger this sort of issue.
If it's possible that importing a random module will trigger some weird failure with
something that is *not* part of the stdlib and I need to figure it out... it can hurt.
In any case: it worked! My first working, useful nim code!
Doing a script with options / parameters
----------------------------------------
In my python version I was using `docopt `__ and this was smooth: there is a
`nim version of docopt `__ and using it was as easy as:
1. `nimble install docopt`
2. :nimrod:`import docopt` in the script
The usage is remarkably similar to python:
.. code:: nimrod
import docopt
when isMainModule:
let doc = """Usage:
boxes