├── .gitignore ├── LICENSE ├── code ├── chapter1_pixels.nim ├── chapter2_ffi_copy.nim ├── chapter2_ffi_nocopy.nim ├── chapter2_overloading.nim └── chapter3_fmt.nim └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andreas Rumpf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /code/chapter1_pixels.nim: -------------------------------------------------------------------------------- 1 | import pixels 2 | 3 | type 4 | Point = object 5 | x: int 6 | y: int 7 | 8 | proc drawHorizontalLine(start: Point; length: Positive) = # <1> 9 | for delta in 0 .. length: # <2> 10 | putPixel(start.x + delta, start.y) # <3> 11 | 12 | proc drawVerticalLine(start: Point; length: Positive) = 13 | for delta in 0 .. length: 14 | putPixel(start.x, start.y + delta) # <4> 15 | 16 | let a = Point(x: 60, y: 40) 17 | 18 | drawHorizontalLine(a, 50) # <5> 19 | drawVerticalLine(a, 30) # <6> 20 | 21 | 22 | 23 | 24 | 25 | # this must be line 25 26 | type 27 | Direction = enum # <1> 28 | Horizontal # <2> 29 | Vertical 30 | 31 | proc drawLine(start: Point; length: Positive; direction: Direction) = # <3> 32 | case direction # <4> 33 | of Horizontal: 34 | drawHorizontalLine(start, length) # <5> 35 | of Vertical: 36 | drawVerticalLine(start, length) 37 | 38 | drawLine(a, 50, Horizontal) # <6> 39 | drawLine(a, 30, Vertical) 40 | 41 | 42 | 43 | 44 | 45 | # this must be line 45 46 | proc drawHorizontalLine(a, b: Point) = # <1> 47 | if b.x < a.x: 48 | drawHorizontalLine(b, a) # <2> 49 | else: 50 | for x in a.x .. b.x: 51 | putPixel(x, a.y) # <3> 52 | 53 | proc drawVerticalLine(a, b: Point) = 54 | if b.y < a.y: 55 | drawVerticalLine(b, a) 56 | else: 57 | for y in a.y .. b.y: 58 | putPixel(a.x, y) 59 | 60 | let 61 | p = Point(x: 20, y: 20) 62 | q = Point(x: 50, y: 20) 63 | r = Point(x: 20, y: -10) 64 | 65 | drawHorizontalLine(p, q) # <4> 66 | drawVerticalLine(p, r) # <5> 67 | 68 | 69 | 70 | # this must be line 70 71 | drawText 30, 40, "Welcome to Nim!", 10, Yellow 72 | 73 | 74 | 75 | # this must be line 75 76 | for i in 1..3: 77 | let texttodraw = "welcome to nim for the " & $i & "th time!" # <1> 78 | drawtext 10, i*10, texttodraw, 8, Yellow # <2> 79 | 80 | 81 | 82 | 83 | 84 | 85 | # this must be line 85 86 | proc putPixels(points: seq[Point]; col: Color) = # <1> 87 | for p in items(points): # <2> 88 | pixels.putPixel p.x, p.y, col # <3> 89 | 90 | putPixels(@[Point(x: 2, y: 3), Point(x: 5, y: 10)], Gold) # <4> 91 | 92 | 93 | 94 | 95 | # this must be line 95 96 | const 97 | ScreenWidth = 1024 # <1> 98 | ScreenHeight = 768 99 | 100 | proc safePutPixel(x, y: int; col: Color) = 101 | if x < 0 or x >= ScreenWidth or 102 | y < 0 or y >= ScreenHeight: 103 | return # <2> 104 | putPixel(x, y, col) # <3> 105 | 106 | 107 | 108 | 109 | 110 | # this must be line 110 111 | template wrap(body: untyped) = # <1> 112 | drawText 0, 10, "Before Body", 8, Yellow # <2> 113 | body # <3> 114 | 115 | wrap: # <4> 116 | for i in 1..3: 117 | let textToDraw = "Welcome to Nim for the " & $i & "th time!" 118 | drawText 10, i*10, textToDraw, 8, Yellow 119 | 120 | 121 | 122 | 123 | 124 | 125 | # this must be line 125 126 | template withColor(col: Color; body: untyped) = # <1> 127 | let colorContext {.inject.} = col # <2> 128 | body 129 | 130 | withColor Blue: # <3> 131 | putPixel 3, 4 # <4> 132 | drawText 10, 10, "abc", 12 -------------------------------------------------------------------------------- /code/chapter2_ffi_copy.nim: -------------------------------------------------------------------------------- 1 | 2 | {.emit: """ 3 | 4 | #include 5 | typedef struct { 6 | double x; 7 | char* s; 8 | } Obj; 9 | 10 | Obj* createObj(const char* s) { 11 | Obj* result = (Obj*) malloc(sizeof(Obj)); 12 | result->x = 40.0; 13 | result->s = malloc(100); 14 | strcpy(result->s, s); 15 | return result; 16 | } 17 | 18 | void destroyObj(Obj* obj) { 19 | free(obj->s); 20 | free(obj); 21 | } 22 | 23 | void useObj(Obj* obj) {} 24 | 25 | double getX(Obj* obj) { return obj->x; } 26 | 27 | """.} 28 | 29 | type 30 | Obj = object 31 | 32 | proc createObj(s: cstring): ptr Obj {.importc, nodecl.} 33 | proc destroyObj(obj: ptr Obj) {.importc, nodecl.} 34 | proc useObj(obj: ptr Obj) {.importc, nodecl.} 35 | proc getX(obj: ptr Obj): float {.importc, nodecl.} 36 | 37 | type 38 | Wrapper = object 39 | obj: ptr Obj 40 | rc: ptr int 41 | 42 | proc `=destroy`(dest: var Wrapper) = 43 | if dest.obj != nil: 44 | if dest.rc[] == 0: 45 | dealloc(dest.rc) 46 | destroyObj(dest.obj) 47 | else: 48 | dec dest.rc[] 49 | 50 | proc `=copy`(dest: var Wrapper; source: Wrapper) = 51 | inc source.rc[] 52 | `=destroy`(dest) 53 | dest.obj = source.obj 54 | dest.rc = source.rc 55 | 56 | proc create(s: string): Wrapper = 57 | Wrapper(obj: createObj(cstring(s)), rc: cast[ptr int](alloc0(sizeof(int)))) 58 | 59 | proc use(w: Wrapper) = useObj(w.obj) 60 | proc getX(w: Wrapper): float = getX(w.obj) 61 | 62 | 63 | proc useWrapper = 64 | let w = @[create("abc"), create("def")] 65 | use w[0] 66 | echo w[1].getX 67 | 68 | useWrapper() 69 | -------------------------------------------------------------------------------- /code/chapter2_ffi_nocopy.nim: -------------------------------------------------------------------------------- 1 | 2 | {.emit: """ 3 | 4 | #include 5 | typedef struct { 6 | double x; 7 | char* s; 8 | } Obj; 9 | 10 | Obj* createObj(const char* s) { 11 | Obj* result = (Obj*) malloc(sizeof(Obj)); 12 | result->x = 40.0; 13 | result->s = malloc(100); 14 | strcpy(result->s, s); 15 | return result; 16 | } 17 | 18 | void destroyObj(Obj* obj) { 19 | free(obj->s); 20 | free(obj); 21 | } 22 | 23 | void useObj(Obj* obj) {} 24 | 25 | double getX(Obj* obj) { return obj->x; } 26 | 27 | """.} 28 | 29 | type 30 | Obj = object 31 | 32 | proc createObj(s: cstring): ptr Obj {.importc, nodecl.} 33 | proc destroyObj(obj: ptr Obj) {.importc, nodecl.} 34 | proc useObj(obj: ptr Obj) {.importc, nodecl.} 35 | proc getX(obj: ptr Obj): float {.importc, nodecl.} 36 | 37 | type 38 | Wrapper = object 39 | obj: ptr Obj 40 | 41 | proc `=destroy`(dest: var Wrapper) = 42 | if dest.obj != nil: destroyObj(dest.obj) 43 | 44 | proc `=copy`(dest: var Wrapper; source: Wrapper) {.error.} 45 | 46 | proc create(s: string): Wrapper = Wrapper(obj: createObj(cstring(s))) 47 | 48 | proc use(w: Wrapper) = useObj(w.obj) 49 | proc getX(w: Wrapper): float = getX(w.obj) 50 | 51 | 52 | proc useWrapper = 53 | let w = @[create("abc"), create("def")] 54 | use w[0] 55 | echo w[1].getX 56 | 57 | useWrapper() 58 | -------------------------------------------------------------------------------- /code/chapter2_overloading.nim: -------------------------------------------------------------------------------- 1 | 2 | import std / [json] 3 | 4 | proc toJ[T: enum](e: T): JsonNode {.inline.} = newJInt(ord(e)) 5 | proc toJ(s: string): JsonNode {.inline.} = newJString(s) 6 | proc toJ(b: bool): JsonNode {.inline.} = newJBool(b) 7 | proc toJ(f: float): JsonNode {.inline.} = newJFloat(f) 8 | proc toJ(i: int): JsonNode {.inline.} = newJInt(i) 9 | 10 | proc toJ[T: object](obj: T): JsonNode = 11 | result = newJObject() 12 | for f, v in fieldPairs(obj): 13 | result[f] = toJ(v) 14 | 15 | proc toJ[T](s: seq[T]): JsonNode = 16 | result = newJArray() 17 | for x in s: 18 | result.add toJ(x) 19 | 20 | proc fromJ[T: enum](t: typedesc[T]; j: JsonNode): T {.inline.} = T(j.getInt) 21 | proc fromJ(t: typedesc[string]; j: JsonNode): string {.inline.} = j.getStr 22 | proc fromJ(t: typedesc[bool]; j: JsonNode): bool {.inline.} = j.getBool 23 | proc fromJ(t: typedesc[int]; j: JsonNode): int {.inline.} = int(j.getInt) 24 | proc fromJ(t: typedesc[float]; j: JsonNode): float {.inline.} = j.getFloat 25 | 26 | proc fromJ[T: seq](t: typedesc[T]; j: JsonNode): T = 27 | result = newSeq[typeof(result[0])]() 28 | assert j.kind == JArray 29 | for elem in items(j): 30 | result.add fromJ(typeof(result[0]), elem) 31 | 32 | proc fromJ[T: object](t: typedesc[T]; j: JsonNode): T = 33 | result = T() 34 | assert j.kind == JObject 35 | for name, loc in fieldPairs(result): 36 | if j.hasKey(name): 37 | loc = fromJ(typeof(loc), j[name]) 38 | -------------------------------------------------------------------------------- /code/chapter3_fmt.nim: -------------------------------------------------------------------------------- 1 | type 2 | TokenKind = enum # <1> 3 | Literal # <2> 4 | NimExpr # <3> 5 | 6 | iterator tokenize(s: string): (TokenKind, string) = # <4> 7 | var i = 0 8 | var tok = Literal # <5> 9 | while i < s.len: 10 | let start = i 11 | case tok 12 | of Literal: 13 | while i < s.len and s[i] != '{': inc i # <6> 14 | of NimExpr: 15 | while i < s.len and s[i] != '}': inc i # <7> 16 | yield (tok, s.substr(start, i-1)) 17 | tok = if tok == Literal: NimExpr else: Literal # <8> 18 | inc i 19 | 20 | 21 | # this must be line 21 22 | 23 | import macros 24 | 25 | macro fmt*(pattern: static[string]): string = # <1> 26 | var args = newTree(nnkBracket) # <2> 27 | for (k, s) in tokenize(pattern): # <3> 28 | case k 29 | of Literal: 30 | if s != "": 31 | args.add newLit(s) # <4> 32 | of NimExpr: 33 | args.add newCall(bindSym"$", parseExpr(s)) # <5> 34 | if args.len == 0: # <6> 35 | result = newLit("") 36 | else: 37 | result = nestList(bindSym"&", args) # <7> 38 | 39 | var x = 0.9 40 | var y = "abc" 41 | 42 | echo fmt"{x} {y}" <8> 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Mastering Nim 2 | 3 | Code examples and issue tracker for my book "Mastering Nim". 4 | 5 | Please note that not every code example is covered here. 6 | --------------------------------------------------------------------------------