The Nim quick reference cheat sheet that aims at providing help on writing basic syntax and methods.
echo "Hello World!"
#---------------------------#
# This is a comment
echo "What's your name? "
var name: string = readLine(stdin)
echo "Hi, ", name, "!"
$ nim c helloworld.nim
# if os is windows then
$ helloworld.exe
# if os is linux then
$ ./Hello_World
# output
Hello, World!
# There is also a possibility
# to both compile and run the
# program with just one command.
# We need to type:
$ nim c -r helloworld.nim
# This is a comment
#[
This is a multiline comment.
In Nim, multiline comments can be nested, beginning with #[
... and ending with ]#
]#
var
letter: char = 'n'
lang = "N" & "im"
nLength: int = len(lang)
boat: float
truth: bool = false
b = 7
c = -11
d = "Hello"
e = '!'
# Use let to declare and bind variables
let
legs = 400
#legs is immutable.
arms = 2_000
# are ignored and are useful for long numbers.
aboutPi = 3.15
let input = readLine(stdin) # works
# Constants are computed at compile
const
debug = true
# performance and is useful in compile time expressions.
compileBadCode = false
Basic data types
et
a = 11
b = 4
echo "a + b = ", a + b
echo "a - b = ", a - b
echo "a * b = ", a * b
echo "a / b = ", a / b
echo "a div b = ", a div b
echo "a mod b = ", a mod b
#output
#a + b = 15
#a - b = 7
#a * b = 44
#a / b = 2.75
#a div b = 2
#a mod b = 3
let
c = 6.75
d = 2.25
echo "c + d = ", c + d
echo "c - d = ", c - d
echo "c * d = ", c * d
echo "c / d = ", c / d
#output
#c + d = 9.0
#c - d = 4.5
#c * d = 15.1875
#c / d = 3.0
#Strings can be described
#as a series of characters.
#Their content is written between two double quotes (").
let
m = "word"
n = "A sentence with interpunction."
o = ""
p = "32"
q = "!"
#Characters are single
#characters. They are
#written between two single quotes (').
let
h = 'z'
i = '+'
j = '2'
k = '35' # error
l = 'xy' # error
#\n is a newline character
#\t is a tab character
#\\ is a backslash (since one \ is used as the escape character)
echo "some\nim\tips"
echo "some\\nim\\tips"
echo r"some\nim\tips"
#output
#some
#im ips
#some\nim\tips
#some\nim\tips
#A boolean (or just bool)
# data type can only have
#two values: true or false.
let isEmpty = true
let isFull = false
let
e = 5
f = 23.987
echo e + f # error
echo float(e)
echo int(f)
echo float(e) + f
echo e + int(f)
#output
#5.0
#23
#28.987
#28
var
p = "abc"
q = "xy"
r = 'z'
p.add("def")
echo "p is now: ", p
q.add(r)
echo "q is now: ", q
echo "concat: ", p & q
echo "p is still: ", p
echo "q is still: ", q
#output
#p is now: abcdef
#q is now: xyz
#concat: abcdefxyz
#p is still: abcdef
#q is still: xyz
let
g = 31
h = 99
echo "g is greater than h: ", g > h
echo "g is smaller than h: ", g < h
echo "g is equal to h: ", g == h
echo "g is not equal to h: ", g != h
echo "g is greater or equal to h: ", g >= h
echo "g is smaller or equal to h: ", g <= h
#output
g is greater than h: false
g is smaller than h: true
g is equal to h: false
g is not equal to h: true
g is greater or equal to h: false
g is smaller or equal to h: true
#example
let
i = 'a'
j = 'd'
k = 'Z'
echo i < j
echo i < k
let
m = "axyb"
n = "axyz"
o = "ba"
p = "ba "
echo m < n
echo n < o
echo o < p
#output
#false
#true
#true
#true
#true
echo "T and T: ", true and true
echo "T and F: ", true and false
echo "F and F: ", false and false
echo "---"
echo "T or T: ", true or true
echo "T or F: ", true or false
echo "F or F: ", false or false
echo "---"
echo "T xor T: ", true xor true
echo "T xor F: ", true xor false
echo "F xor F: ", false xor false
echo "---"
echo "not T: ", not true
echo "not F: ", not false
#output
#T and T: true
#T and F: false
#F and F: false
---
#T or T: true
#T or F: true
#F or F: false
---
#T xor T: false
#T xor F: true
#F xor F: false
---
#not T: false
#not F: true
let
a = 11
b = 22
c = 999
if a < b:
echo "a is smaller than b"
if 10*a < b:
echo "not only that, a is *much* smaller than b"
if b < c:
echo "b is smaller than c"
if 10*b < c:
echo "not only that, b is *much* smaller than c"
if a+b > c:
echo "a and b are larger than c"
if 1 < 100 and 321 > 123:
echo "did you know that 1 is smaller than 100?"
echo "and 321 is larger than 123! wow!"
#output
#a is smaller than b
#b is smaller than c
#not only that, b is *much* smaller than c
let name = readLine(stdin)
case name
of "":
echo "Poor soul, you lost your name?"
of "name":
echo "Very funny, your name is name."
of "Dave", "Frank":
echo "Cool name!"
else:
echo "Hi, ", name, "!"
echo "What's your name? "
var name = readLine(stdin)
while name == "":
echo "Please tell me your name: "
name = readLine(stdin) # no `var`, because we do not declare a new variable here
echo "Counting to ten: "
for i in countup(1, 10):
echo i
# --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
while false:
var x = "hi"
echo x # does not work
#-----------------------#
block myblock:
var x = "hi"
echo x # does not work either
block myblock:
echo "entering block"
while true:
echo "looping"
break # leaves the loop, but not the block
echo "still in block"
echo "outside the block"
for i in 1 .. 5:
if i <= 3: continue
echo i # will only print 4 and 5
when system.hostOS == "windows":
echo "running on Windows!"
elif system.hostOS == "linux":
echo "running on Linux!"
elif system.hostOS == "macosx":
echo "running on Mac OS X!"
else:
echo "unknown operating system"
# no indentation needed for single-assignment statement:
if x: x = false
# indentation needed for nested if statement:
if x:
if y:
y = false
else:
y = true
# indentation needed, because two statements follow the condition:
if x:
x = false
y = false
proc yes(question: string): bool =
echo question, " (y/n)"
while true:
case readLine(stdin)
of "y", "Y", "yes", "Yes": return true
of "n", "N", "no", "No": return false
else: echo "Please be clear: yes or no"
if yes("Should I delete all your important files?"):
echo "I'm sorry Dave, I'm afraid I can't do that."
else:
echo "I think you know what the problem is just as well as I do."
proc sumTillNegative(x: varargs[int]): int =
for i in x:
if i < 0:
return
result = result + i
echo sumTillNegative() # echoes 0
echo sumTillNegative(3, 4, 5) # echoes 12
echo sumTillNegative(3, 4 , -1 , 6) # echoes 7
proc printSeq(s: seq, nprinted: int = -1) =
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
for i in 0 ..< nprinted:
echo s[i]
#------------------- #
proc divmod(a, b: int; res, remainder: var int) =
res = a div b # integer division
remainder = a mod b # integer modulo operation
var
x, y: int
divmod(8, 5, x, y) # modifies x and y
echo x
echo y
discard yes("May I ask a pointless question?")
proc p(x, y: int): int {.discardable.} =
return x + y
p(3, 4) # now valid
proc createWindow(x, y, width, height: int; title: string; show: bool): Window = ...
var w = createWindow(show = true, title = "My Aplication", x = 0, y = 0, height = 600, width = 800)
var w = createWindow(0, 0, title = "My Application",
height = 600, width = 800, true)
proc createWindow(x = 0, y = 0, width = 500, height = 700,
title = "unknown",
show = true): Window =
var w = createWindow(title = "My Application", height = 600, width = 800)
proc toString(x: int): string =
result =
if x < 0: "negative"
elif x > 0: "positive"
else: "zero"
proc toString(x: bool): string =
result =
if x: "yep"
else: "nope"
assert toString(13) == "positive"
# calls the toString(x: int) proc
assert toString(true) == "yep"
# calls the toString(x: bool) proc
proc odd(n: int): bool =
assert(n >= 0) # makes sure we don't run into negative recursion
if n == 0: false
else:
n == 1 or even(n-1)
proc even(n: int): bool =
assert(n >= 0) # makes sure we don't run into negative recursion
if n == 1: false
else:
n == 0 or odd(n-1)
echo "Counting to ten: "
for i in countup(1, 10):
echo i
proc countup(a, b: int): int =
var res = a
while res <= b:
return res
inc(res)
iterator countup(a, b: int): int =
var res = a
while res <= b:
yield res
inc(res)
var
x: int32 = 1.int32 # same as calling int32(1)
y: int8 = int8('a') # 'a' == 97'i8
z: float = 2.5 # int(2.5) rounds down to 2
sum: int = int(x) + int(y) + int(z) # sum == 100
var
myBool = true
myCharacter = 'n'
myString = "nim"
myInteger = 42
myFloat = 3.14
echo myBool, ":", repr(myBool)
# --> true:true
echo myCharacter, ":", repr(myCharacter)
# --> n:'n'
echo myString, ":", repr(myString)
# --> nim:0x10fa8c050"nim"
echo myInteger, ":", repr(myInteger)
# --> 42:42
echo myFloat, ":", repr(myFloat)
# --> 3.14:3.14
type
Direction = enum
north, east, south, west
var x = south
# `x` is of type `Direction`; its value is `south`
#prints "south"
echo x
Operation | Comment |
---|---|
ord(x) | returns the integer value that is used to represent x's value |
inc(x) | increments x by one |
inc(x, n) | increments x by n; n is an integer |
dec(x) | decrements x by one |
dec(x, n) | decrements x by n; n is an integer |
succ(x) | returns the successor of x |
succ(x, n) | returns the n'th successor of x |
pred(x) | returns the predecessor of x |
pred(x, n) | returns the n'th predecessor of x |
type
MySubrange = range[0..5]
var s: set[int64]
# Error: set is too large; use `std/sets` for ordinal types
# with more than 2^16 elements
type
CharSet = set[char]
var
x: CharSet
x = {'a'..'z', '0'..'9'}
# This constructs a set that contains the
# letters from 'a' to 'z' and the digits
# from '0' to '9'
type
MyFlag* {.size: sizeof(cint).} = enum
A
B
C
D
MyFlags = set[MyFlag]
proc toNum(f: MyFlags): int = cast[cint](f)
proc toFlags(v: int): MyFlags = cast[MyFlags](v)
assert toNum({}) == 0
assert toNum({A}) == 1
assert toNum({D}) == 8
assert toNum({A, C}) == 5
assert toFlags(0) == {}
assert toFlags(7) == {A, B, C}
type
IntArray = array[0..5, int] # an array that is indexed with 0..5
var
x: IntArray
x = [1, 2, 3, 4, 5, 6]
for i in low(x) .. high(x):
echo x[i]
var
x: seq[int] # a reference to a sequence of integers
x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
for value in @[3, 4, 5]:
echo value
# --> 3
# --> 4
# --> 5
for i, value in @[3, 4, 5]:
echo "index: ", $i, ", value:", $value
# --> index: 0, value:3
# --> index: 1, value:4
# --> index: 2, value:5
var
fruits: seq[string]
# reference to a sequence of strings that is initialized with '@[]'
capitals: array[3, string]
# array of strings with a fixed size
capitals = ["New York", "London", "Berlin"]
# array 'capitals' allows assignment of only three elements
fruits.add("Banana")
# sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango")
proc openArraySize(oa: openArray[string]): int =
oa.len
assert openArraySize(fruits) == 2
# procedure accepts a sequence as parameter
assert openArraySize(capitals) == 3
# but also an array type
proc myWriteln(f: File, a: varargs[string]) =
for s in items(a):
write(f, s)
write(f, "\n")
myWriteln(stdout, "abc", "def", "xyz")
# is transformed by the compiler to:
myWriteln(stdout, ["abc", "def", "xyz"])
var
a = "Nim is a programming language"
b = "Slices are useless."
echo a[7 .. 12] # --> 'a prog'
b[11 .. ^2] = "useful"
echo b # --> 'Slices are useful.'
type
Person = object
name: string
age: int
var person1 = Person(name: "Peter", age: 30)
echo person1.name # "Peter"
echo person1.age # 30
var person2 = person1 # copy of person 1
person2.age += 14
echo person1.age # 30
echo person2.age # 44
# the order may be changed
let person3 = Person(age: 12, name: "Quentin")
# not every member needs to be specified
let person4 = Person(age: 3)
# unspecified members will be initialized with their default
# values. In this case it is the empty string.
doAssert person4.name == ""
type
Person = tuple
name: string
age: int
PersonX = tuple[name: string, age: int]
PersonY = (string, int)
var
person: Person
personX: PersonX
personY: PersonY
person = (name: "Peter", age: 30)
# Person and PersonX are equivalent
personX = person
# Create a tuple with anonymous fields:
personY = ("Peter", 30)
person = personY
personY = person
person = ("Peter", 30)
echo person.name # "Peter"
echo person.age # 30
echo person[0] # "Peter"
echo person[1] # 30
var building: tuple[street: string, number: int]
building = ("Rue del Percebe", 13)
echo building.street
type
Node = ref object
le, ri: Node
data: int
var n = Node(data: 9)
echo n.data
# no need to write n[].data; in fact n[].data is highly discouraged!
proc greet(name: string): string =
"Hello, " & name & "!"
proc bye(name: string): string =
"Goodbye, " & name & "."
proc communicate(greeting: proc (x: string): string, name: string) =
echo greeting(name)
communicate(greet, "John")
communicate(bye, "Mary")
# Module A
var
x*, y: int
proc `*` *(a, b: seq[int]): seq[int] =
# allocate a new sequence:
newSeq(result, len(a))
# multiply two int sequences:
for i in 0 ..< len(a): result[i] = a[i] * b[i]
when isMainModule:
# test the new `*` operator for sequences:
assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
import mymodule except y
# From statement
from mymodule import x, y, z
from mymodule import x, y, z
x()
# use x without any qualification
#Include statement
include fileA, fileB, fileC
type
Person = ref object of RootObj
name*: string # the * means that `name` is accessible from other modules
age: int # no * means that the field is hidden from other modules
Student = ref object of Person # Student inherits from Person
id: int # with an id field
var
student: Student
person: Person
assert(student of Student) # is true
# object construction:
student = Student(name: "Anton", age: 5, id: 2)
echo student[]
type
Node = ref object
# a reference to an object with the following field:
le, ri: Node
# left and right subtrees
sym: ref Sym
# leaves contain a reference to a Sym
Sym = object
# a symbol
name: string
# the symbol's name
line: int
# the line the symbol was declared in
code: Node
# the symbol's abstract syntax tree
# This is an example how an abstract syntax tree could be modelled in Nim
type
NodeKind = enum # the different node types
nkInt, # a leaf with an integer value
nkFloat, # a leaf with a float value
nkString, # a leaf with a string value
nkAdd, # an addition
nkSub, # a subtraction
nkIf # an if statement
Node = ref object
case kind: NodeKind # the `kind` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
of nkString: strVal: string
of nkAdd, nkSub:
leftOp, rightOp: Node
of nkIf:
condition, thenPart, elsePart: Node
var n = Node(kind: nkFloat, floatVal: 1.0)
# the following statement raises an `FieldDefect` exception, because
# n.kind's value does not fit:
n.strVal = ""
import std/strutils
echo "abc".len # is the same as echo len("abc")
echo "abc".toUpperAscii()
echo({'a', 'b', 'c'}.card)
stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
##############################
import std/[strutils, sequtils]
stdout.writeLine("Give a list of numbers (separated by spaces): ")
stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`)
stdout.writeLine(" is the maximum!")
type
Socket* = ref object of RootObj
h: int # cannot be accessed from the outside of the module due to missing star
proc `host=`*(s: var Socket, value: int) {.inline.} =
## setter of host address
s.h = value
proc host*(s: Socket): int {.inline.} =
## getter of host address
s.h
var s: Socket
new s
s.host = 34 # same as `host=`(s, 34)
type
Vector* = object
x, y, z: float
proc `[]=`* (v: var Vector, i: int, value: float) =
# setter
case i
of 0: v.x = value
of 1: v.y = value
of 2: v.z = value
else: assert(false)
proc `[]`* (v: Vector, i: int): float =
# getter
case i
of 0: result = v.x
of 1: result = v.y
of 2: result = v.z
else: assert(false)
type
Expression = ref object of RootObj ## abstract base class for an expression
Literal = ref object of Expression
x: int
PlusExpr = ref object of Expression
a, b: Expression
# watch out: 'eval' relies on dynamic binding
method eval(e: Expression): int {.base.} =
# override this base method
quit "to override!"
method eval(e: Literal): int = e.x
method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
proc newLit(x: int): Literal = Literal(x: x)
proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
var
e: ref OSError
new(e)
e.msg = "the request to the OS failed"
raise e
raise newException(OSError, "the request to the OS failed")
from std/strutils import parseInt
# read the first two lines of a text file that should contain numbers
# and tries to add them
var
f: File
if open(f, "numbers.txt"):
try:
let a = readLine(f)
let b = readLine(f)
echo "sum: ", parseInt(a) + parseInt(b)
except OverflowDefect:
echo "overflow!"
except ValueError:
echo "could not convert string to integer"
except IOError:
echo "IO error!"
except CatchableError:
echo "Unknown exception!"
# reraise the unknown exception:
raise
finally:
close(f)
proc complexProc() {.raises: [IOError, ArithmeticDefect].} =
proc simpleProc() {.raises: [].} =
...
type
BinaryTree*[T] = ref object # BinaryTree is a generic type with
# generic param `T`
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
new(result)
result.data = data
proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
# insert a node into the tree
if root == nil:
root = n
else:
var it = root
while it != nil:
# compare the data items; uses the generic `cmp` proc
# that works for any type that has a `==` and `<` operator
var c = cmp(it.data, n.data)
if c < 0:
if it.le == nil:
it.le = n
return
it = it.le
else:
if it.ri == nil:
it.ri = n
return
it = it.ri
proc add*[T](root: var BinaryTree[T], data: T) =
# convenience proc:
add(root, newNode(data))
iterator preorder*[T](root: BinaryTree[T]): T =
# Preorder traversal of a binary tree.
# This uses an explicit stack (which is more efficient than
# a recursive iterator factory).
var stack: seq[BinaryTree[T]] = @[root]
while stack.len > 0:
var n = stack.pop()
while n != nil:
yield n.data
add(stack, n.ri) # push right subtree onto the stack
n = n.le # and follow the left pointer
var
root: BinaryTree[string] # instantiate a BinaryTree with `string`
add(root, newNode("hello")) # instantiates `newNode` and `add`
add(root, "world") # instantiates the second `add` proc
for str in preorder(root):
stdout.writeLine(str)
template `!=` (a, b: untyped): untyped =
# this definition exists in the System module
not (a == b)
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
#############################################
const
debug = true
proc log(msg: string) {.inline.} =
if debug: stdout.writeLine(msg)
var
x = 4
log("x has the value: " & $x)
####################################
template withFile(f: untyped, filename: string, mode: FileMode,
body: untyped) =
let fn = filename
var f: File
if open(f, fn, mode):
try:
body
finally:
close(f)
else:
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
txt.writeLine("line 1")
txt.writeLine("line 2")
import std/macros
macro myMacro(arg: static[int]): untyped =
echo arg # just an int (7), not `NimNode`
myMacro(1 + 2 * 3)
echo "Hello ":
let a = "Wor"
let b = "ld!"
a & b
dumpTree:
var mt: MyType = MyType(a:123.456, b:"abcdef")
# output:
# StmtList
# VarSection
# IdentDefs
# Ident "mt"
# Ident "MyType"
# ObjConstr
# Ident "MyType"
# ExprColonExpr
# Ident "a"
# FloatLit 123.456
# ExprColonExpr
# Ident "b"
# StrLit "abcdef"
macro myAssert(arg: untyped): untyped =
arg.expectKind nnkInfix
import std/macros
type
MyType = object
a: float
b: string
macro myMacro(arg: untyped): untyped =
var mt: MyType = MyType(a:123.456, b:"abcdef")
#
let mtLit = newLit(mt)
result = quote do:
echo `arg`
echo `mtLit`
myMacro("Hallo")
import std/macros
macro myAssert(arg: untyped): untyped =
# all node kind identifiers are prefixed with "nnk"
arg.expectKind nnkInfix
arg.expectLen 3
# operator as string literal
let op = newLit(" " & arg[0].repr & " ")
let lhs = arg[1]
let rhs = arg[2]
result = quote do:
if not `arg`:
raise newException(AssertionDefect,$`lhs` & `op` & $`rhs`)
let a = 1
let b = 2
myAssert(a != b)
myAssert(a == b)