├── README.md ├── dynamic_web_pages ├── build.nims ├── cross.nims ├── mweb.nim ├── nim.cfg ├── rage.md ├── templateweb.nim ├── uni.nim ├── visitors.txt ├── web.nim └── webdecode.nim ├── nimacros ├── Makefile ├── chaps │ ├── nimdoc1.md │ ├── nimdoc2.md │ └── nimdoc3.md ├── figs-prefix │ ├── bugcerto.jpg │ ├── bugcerto.png │ ├── minibuffer.jpg │ ├── neatsum.jpg │ ├── piggy.jpg │ ├── readyforaction.jpg │ ├── stackshark.jpg │ └── webpage.jpg ├── nimdoc.md ├── nimdoc.pdf ├── nimrod.xml └── src │ ├── akavelIter.nim │ ├── akavelLoop.nim │ ├── bin │ └── README.md │ ├── build.nims │ ├── cmd.nim │ ├── commentest.nim │ ├── cons.nim │ ├── consp.nim │ ├── consproc.nim │ ├── consrpn.nim │ ├── costumary.nim │ ├── counter.nim │ ├── csv.data │ ├── csv.nim │ ├── excrd.nim │ ├── handsexpr.nim │ ├── hello.nim │ ├── hello.nims │ ├── indentest.nim │ ├── infixLoop.nim │ ├── ird.nim │ ├── iterfib.nim │ ├── lispmacros.nim │ ├── nums.data │ ├── powertwo.nim │ ├── quoteLoop.nim │ ├── rd.nim │ ├── rd.py │ ├── rdline.nim │ ├── rdwrt.nim │ ├── recfib.nim │ ├── rep.lisp │ ├── rep.nim │ ├── rpn.nim │ ├── sexpr.nim │ ├── slowPowertwo.nim │ ├── sugarLoop.nim │ ├── twodates.nim │ ├── units.nim │ └── zeller.nim └── nimdoc.pdf /README.md: -------------------------------------------------------------------------------- 1 | # nimacros 2 | Documentation for Nim Macros 3 | 4 | This work assumes that the reader already knows how to program and is familiar with command line tools. Therefore, as far as programming goes, the only subject that the author discusses is macro designing. Below, you will learn how to build the documentation for Nim macros, by using pandoc. For the convenience of those who don't want to install pandoc, here is a link to the Portable Document Format (PDF) version of the file: 5 | 6 | [Documentation for Nim macros](https://github.com/FemtoEmacs/nimacros/blob/master/nimdoc.pdf) 7 | 8 | If you have a snippet that can help with the understanding a concept that was not well explained in this tutorial, please send us your snippet. This is a collective work that depends on your collaboration. 9 | 10 | ## Installation 11 | 12 | The Nim programming language is very easy to install. Follow the instructions on the link below, and you cannot go wrong or even astray. 13 | 14 | [Documentation for Nim macros](https://nim-lang.org/install.html) 15 | 16 | ## Tutorial 17 | 18 | The goal of this project is to prepare documentation on Nim macros. You will find two things in this repository. The first one is a Portable Document Format (PDF) file, where you can learn how to write macros in Nim. It is work in progress, therefore you will find many spelling mistakes, grammar errors and, what is worse, incomplete explanations. I hope that these shortcomings will disappear with time and hard work from my collaborators. The other things you will find here are short examples, that you can compile with the help of a Makefile. Let us suppose that you want to compile the `rdwrt.nim` source code file, that implements an emulator for an HP calculator. From the `src` folder, type the following command: 19 | 20 | ```Shell 21 | ~/nim/nimacros/src master × 22 | › make APP=rdwrt 23 | nim c -o:rdwrt.x -d:danger --hints:off --nimcache:lixo rdwrt.nim 24 | CC: stdlib_formatfloat.nim 25 | CC: stdlib_io.nim 26 | CC: stdlib_system.nim 27 | CC: stdlib_parseutils.nim 28 | CC: stdlib_strutils.nim 29 | CC: stdlib_posix.nim 30 | CC: stdlib_times.nim 31 | CC: stdlib_os.nim 32 | CC: rdwrt.nim 33 | 34 | ~/nim/nimacros/src master × 35 | › ./rdwrt.x 36 | > 3 4 x 5 6 x + 37 | 42.0 38 | ``` 39 | 40 | You can also modify the `nimdoc.md`, which is written in the `Markdown` format. There is an application called *pandoc* that compiles `Markdown` to PDF. The compilation from the `Markdown` format to PDF is also achieved through a Makefile script, as shown below: 41 | 42 | ```Shell 43 | ~/nim/nimacros master × 44 | › make D=nimdoc 45 | pandoc -V geometry:margin=1in -V documentclass=report\ 46 | chaps/*.md nimdoc.md --syntax-definition\ 47 | nimrod.xml -o nimdoc.pdf 48 | ``` 49 | 50 | ## Nim Language 51 | The Nim programming language has a syntax that is similar to the one you will find in the Python programming langauge. This syntax is recognised as easy to learn, friendly, and therefore efficent to write code in. The main difference between Nim and Python is that Nim generates fast code with small runtime and heap consumption. The Python programming language is instead interpreted, so generally requires the Python runtime to be installed on a computer too, before the programer's code can be run. Nim code however compiles into a single binary file, and does not require any addtional Nim runtime to be installed on a computer, before that binary is exectuted. The complied Nim program usually runs many times faster that the equivilent Python program as well, which is always a nice benefit to have. 52 | 53 | Macro is a tool that you can use for tailoring the language for the application. If the language is enriched with macros it can help model a problem well, so the developer will potentially find a solution without errors in less time. 54 | 55 | -------------------------------------------------------------------------------- /dynamic_web_pages/build.nims: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S nim --hints:off 2 | mode = ScriptMode.Silent 3 | from os import `/` 4 | if paramCount() > 2 and fileExists(paramStr(3) & ".nim"): 5 | let 6 | app = paramStr(3) 7 | src = app & ".nim" 8 | exe = app & ".x" 9 | c = "nim c --hints:off --nimcache:xx -d:danger -o:" 10 | 11 | exec c & exe & " " & src 12 | echo c & exe & " " & src 13 | mvFile exe, "bin" / exe 14 | else: echo "Usage: ./build.nims " 15 | 16 | -------------------------------------------------------------------------------- /dynamic_web_pages/cross.nims: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S nim --hints:off 2 | mode = ScriptMode.Silent 3 | if paramCount() > 2 and fileExists(paramStr(3) & ".nim"): 4 | let 5 | app = paramStr(3) 6 | src = app & ".nim" 7 | exe = "nim" & app & ".n " 8 | c = "nim c --nimcache:xx --os:linux --cpu:amd64 -d:release -o:" 9 | cc= " --passL:\"-static\"" 10 | exec c & exe & " " & cc & " " & src 11 | echo c & exe & " " & cc & " " & src 12 | else: echo "Usage: ./build.nims " 13 | 14 | -------------------------------------------------------------------------------- /dynamic_web_pages/mweb.nim: -------------------------------------------------------------------------------- 1 | # File: web.nim 2 | import strutils, os, strscans, macros 3 | let input = open("rage.md") 4 | 5 | let form = """ 6 |

7 | 8 |
9 |

10 | """ 11 | 12 | echo "Content-type: text/html\n\n" 13 | echo """ 14 | 15 | 17 | 18 | """ 19 | echo "" 20 | 21 | proc rest(input: string; match: var string, start: int): int = 22 | ## matches until the end of the string 23 | match = input[start .. input.high] 24 | # result is either 1 (string is empty) or the number of found chars 25 | result = max(1, input.len - start) 26 | 27 | macro match(args, line: typed): untyped = 28 | ## match the `args` via `scanf` in `line`. `args` must be a `[]` of 29 | ## `(scanf string matcher, replacement string)` tuples, where the latter 30 | ## has to include a single `$#` to indicate the position of the replacement. 31 | ## The order of the `args` is important, since an if statement is built. 32 | let argImpl = args.getImpl 33 | expectKind argImpl, nnkBracket 34 | result = newStmtList() 35 | let matched = genSym(nskVar, "matched") 36 | result.add quote do: 37 | var `matched`: string 38 | var ifStmt = nnkIfStmt.newTree() 39 | for el in argImpl: 40 | expectKind el, nnkTupleConstr 41 | let toMatch = el[0] 42 | let toReplace = el[1] 43 | let ifBody = nnkStmtList.newTree(nnkCall.newTree(ident"echo", 44 | nnkCall.newTree(ident"%", 45 | toReplace, 46 | matched)), 47 | nnkAsgn.newTree(matched, newLit(""))) 48 | let ifCond = nnkCall.newTree(ident"scanf", line, toMatch, matched) 49 | ifStmt.add nnkElifBranch.newTree(ifCond, ifBody) 50 | result.add ifStmt 51 | echo result.repr 52 | 53 | const h1title = ("# ${rest}", "

$#

") 54 | const h2title = ("## ${rest}", "

$#

") 55 | const elseLine = ("${rest}", "$#
") 56 | const replacements = [h1title, h2title, elseLine] 57 | for line in input.lines: 58 | match(replacements, line) 59 | # produces: 60 | # var matched: string 61 | # if scanf("# ${rest}", line, matched): 62 | # echo h1title[1] % matched 63 | # if scanf("## ${rest}", line, matched): 64 | # echo h2title[1] % matched 65 | # if scanf("${rest}", line, matched): 66 | # echo elseLine[1] % matched 67 | echo form 68 | 69 | let qs = getEnv("QUERY_STRING", "none").split({'+'}).join(" ") 70 | if qs != "none" and qs.len > 0: 71 | let output = open("visitors.txt", fmAppend) 72 | write(output, qs&"\n") 73 | output.close 74 | 75 | let inputVisitors= open("visitors.txt") 76 | for line in inputVisitors.lines: 77 | match(replacements, line) 78 | inputVisitors.close 79 | echo "" 80 | input.close 81 | -------------------------------------------------------------------------------- /dynamic_web_pages/nim.cfg: -------------------------------------------------------------------------------- 1 | amd64.linux.gcc.path:"/usr/local/Cellar/musl-cross/0.9.8/bin" 2 | amd64.linux.gcc.exe:"x86_64-linux-musl-gcc" 3 | amd64.linux.gcc.linkerexe:"x86_64-linux-musl-gcc" 4 | 5 | -------------------------------------------------------------------------------- /dynamic_web_pages/rage.md: -------------------------------------------------------------------------------- 1 | # The Rage of Achilles 2 | 3 | Rage—Goddess, sing the rage of Peleus' son Achilles, 4 | murderous, doomed, that cost the Achaeans countless losses, 5 | 6 | hurling down to the House of Death so many sturdy souls, 7 | great fighters' souls, but made their bodies carrion, 8 | feasts for the dogs and birds, 9 | and the will of Zeus was moving toward its end. 10 | Begin, Muse, when the two first broke and clashed, 11 | Agamemnon lord of men and brilliant Achilles. 12 | 13 | ## Visitors 14 | in: Nom_de_guerre 15 | 16 | -------------------------------------------------------------------------------- /dynamic_web_pages/templateweb.nim: -------------------------------------------------------------------------------- 1 | # File: web.nim 2 | import strutils, os, strscans, unicode, uri 3 | let input = open("rage.md") 4 | 5 | let form = """ 6 |
7 | 8 |

9 | """ 10 | 11 | echo "Content-type: text/html\n\n" 12 | echo """ 13 | 14 | 16 | 17 | 18 | """ 19 | 20 | proc tl(input: string; match: var string, start: int): int = 21 | ## matches until the end of the string 22 | match = input[start .. input.high] 23 | # result is either 1 (string is empty) or the number of found chars 24 | result = max(1, input.len - start) 25 | 26 | template `>>` (a,b:untyped): untyped= echo a % b 27 | 28 | var html: string 29 | for line in input.lines: 30 | if scanf(line, "## ${tl}", html): "

$1

" >> html 31 | elif scanf(line, "# ${tl}", html): "

$1

" >> html 32 | elif scanf(line, "in: ${tl}", html): form >> html 33 | elif scanf(line, "${tl}", html): "$#
" >> html 34 | 35 | let qs = getEnv("QUERY_STRING", "none") 36 | if qs != "none" and qs.len > 0: 37 | let output = open("visitors.txt", fmAppend) 38 | if scanf(qs, "N=${tl}", html): 39 | let n= "$#" % html 40 | if n != "" and not isSpace(n): 41 | write(output, n.decodeUrl(true) & "\n") 42 | output.close 43 | 44 | let inputVisitors= open("visitors.txt") 45 | for line in inputVisitors.lines: echo line&"
" 46 | inputVisitors.close 47 | echo "" 48 | input.close 49 | 50 | -------------------------------------------------------------------------------- /dynamic_web_pages/uni.nim: -------------------------------------------------------------------------------- 1 | # File: web.nim 2 | import strutils, os, strscans, unicode, uri, unidecode 3 | let input = open("rage.md") 4 | let form = """ 5 |
6 | Visitor: 7 |

8 | """ 9 | 10 | echo "Content-type: text/html\n\n" 11 | echo """ 12 | 13 | 15 | 16 | 17 | """ 18 | 19 | proc tl(input: string; match: var string, start: int): int = 20 | match = input[start .. input.high] 21 | result = max(1, input.len - start) 22 | 23 | var html: string 24 | for line in input.lines: 25 | if scanf(line, "## ${tl}", html): echo "

$1

" % html 26 | elif scanf(line, "# ${tl}", html): echo "

$1

" % html 27 | elif scanf(line, "in: ${tl}", html): echo form % html 28 | elif scanf(line, "${tl}", html): echo "$#
" % html 29 | 30 | let qs = getEnv("QUERY_STRING", "none") 31 | if qs != "none" and qs.len > 0: 32 | let vs = open("visitors.txt", fmAppend) 33 | if scanf(qs, "N=${tl}", html): 34 | let n= "$#" % html 35 | if n != "" and not isSpace(n): 36 | let un = n.decodeUrl(true) 37 | let ud = unidecode(un) 38 | if un == ud: write(vs, un & "\n") 39 | else: write(vs, un & "(" & ud & ")\n") 40 | vs.close 41 | 42 | let inputVisitors= open("visitors.txt") 43 | for line in inputVisitors.lines: echo line&"
" 44 | inputVisitors.close 45 | echo "" 46 | input.close 47 | 48 | -------------------------------------------------------------------------------- /dynamic_web_pages/visitors.txt: -------------------------------------------------------------------------------- 1 | Ze 2 | -------------------------------------------------------------------------------- /dynamic_web_pages/web.nim: -------------------------------------------------------------------------------- 1 | # File: web.nim 2 | import strutils, os, strscans, unicode, uri 3 | let input = open("rage.md") 4 | let form = """ 5 |
6 | Visitor: 7 |

8 | """ 9 | 10 | echo "Content-type: text/html\n\n" 11 | echo """ 12 | 13 | 15 | 16 | 17 | """ 18 | 19 | proc tl(input: string; match: var string, start: int): int = 20 | match = input[start .. input.high] 21 | result = max(1, input.len - start) 22 | 23 | var html: string 24 | for line in input.lines: 25 | if scanf(line, "## ${tl}", html): echo "

$1

" % html 26 | elif scanf(line, "# ${tl}", html): echo "

$1

" % html 27 | elif scanf(line, "in: ${tl}", html): echo form % html 28 | elif scanf(line, "${tl}", html): echo "$#
" % html 29 | 30 | let qs = getEnv("QUERY_STRING", "none") 31 | if qs != "none" and qs.len > 0: 32 | let vs = open("visitors.txt", fmAppend) 33 | if scanf(qs, "N=${tl}", html): 34 | let n= "$#" % html 35 | if n != "" and not isSpace(n): write(vs, n.decodeUrl(true) & "\n") 36 | vs.close 37 | 38 | let inputVisitors= open("visitors.txt") 39 | for line in inputVisitors.lines: echo line&"
" 40 | inputVisitors.close 41 | echo "" 42 | input.close 43 | 44 | -------------------------------------------------------------------------------- /dynamic_web_pages/webdecode.nim: -------------------------------------------------------------------------------- 1 | proc webcode(s: string): string = 2 | case s 3 | of "C3%A1" : "á" 4 | of "C3%A9" : "é" 5 | of "C3%AD" : "í" 6 | of "C3%B3" : "ó" 7 | of "C3%BA" : "ú" 8 | of "C3%A3" : "ã" 9 | of "C3%B1" : "ñ" 10 | of "C3%AA" : "ê" 11 | of "C3%B4" : "ô" 12 | of "C3%B5" : "õ" 13 | of "C3%A2" : "â" 14 | of "C3%AE" : "î" 15 | of "C3%BB" : "û" 16 | of "C3%A8" : "è" 17 | of "C3%A0" : "à" 18 | of "C3%A7" : "ç" 19 | of "D0%90" : "А" 20 | of "D0%B4" : "д" 21 | of "D0%B8" : "и" 22 | of "D0%BB" : "л" 23 | of "D1%8F" : "я" 24 | of "D1%81" : "с" 25 | of "D0%BA" : "к" 26 | of "D0%B0" : "а" 27 | of "D0%9A" : "К" 28 | of "D0%BE" : "о" 29 | of "D1%82" : "т" 30 | of "D0%B2" : "в" 31 | of "D0%A0" : "Р" 32 | of "D0%B3" : "г" 33 | of "D0%BD" : "н" 34 | of "C3%84" : "Ä" 35 | of "C3%A4" : "ä" 36 | of "C3%96" : "Ö" 37 | of "C3%B6" : "ö" 38 | of "C3%9C" : "Ü" 39 | of "C3%BC" : "ü" 40 | of "C3%9F" : "ß" 41 | of "C3%85" : "Å" 42 | of "C3%A5" : "å" 43 | else: s 44 | 45 | proc code*(s: string) : string = 46 | result= "" 47 | var i= 0 48 | while i < s.len: 49 | if s[i] == '%' and i+6 <= s.len: 50 | result = result & webcode(s[i+1..i+5]) 51 | i= i+6 52 | else: 53 | result.add(s[i]) 54 | i= i+1 55 | 56 | ## echo code("ab%C3%B3%C3%A7") 57 | 58 | -------------------------------------------------------------------------------- /nimacros/Makefile: -------------------------------------------------------------------------------- 1 | D= 2 | PDF_ARGS = -V geometry:margin=1in -V documentclass=report 3 | SYNTAX= --syntax-definition nimrod.xml 4 | ENG= --pdf-engine=xelatex 5 | 6 | ifeq ($(D),) 7 | abort: 8 | @echo Usage: make D=document 9 | endif 10 | 11 | all: 12 | pandoc $(PDF_ARGS) chaps/*.md $(D).md $(SYNTAX) -o $(D).pdf 13 | clean: 14 | rm -rf $(D).pdf 15 | 16 | -------------------------------------------------------------------------------- /nimacros/chaps/nimdoc1.md: -------------------------------------------------------------------------------- 1 | 2 | \usetikzlibrary{backgrounds, positioning, %% Draw Y-chart 3 | snakes, fit, %% Draw Y-chart 4 | 3d, arrows, automata, shapes.gates.logic.US, 5 | shapes.gates.logic.IEC, calc, 6 | circuits.logic.US} 7 | 8 | 9 | \mainmatter 10 | # Hello, World! 11 | 12 | Since you are reading this tutorial, I assume that you 13 | want to learn the Nim computer language. It is a long 14 | tradition in Computer Science to start a tutorial 15 | concerning programming with a demo that shows how to 16 | output messages to a terminal. However, you must learn 17 | many things before actually being able to make such a demo 18 | work. Therefore, I suggest that you call a geek who has 19 | majored in Computer Science to run the applications presented 20 | in this first chapter, which will deal with sending messages 21 | for printing on a terminal. I will use exercises created by 22 | Kaushal Modi to get your attention, and entice you into 23 | Nim programming. Here is the first program: 24 | 25 | ```Nim 26 | import os, std/[Terminal] 27 | 28 | styledEcho "Hello, ", styleBright, fgCyan, paramStr(1) 29 | ``` 30 | 31 | 32 | Let us assume that the geek, who you hired to coach you 33 | through this demo, has discovered a way of writing the 34 | above program into the `hi.nim` file. For instance, she 35 | could use the `cat` command as shown below. 36 | 37 | ```Nim 38 | › cat < hi.nim 39 | import os, std/[Terminal] 40 | styledEcho "Hi, ", styleItalic, fgGreen, paramStr(1) 41 | EOT 42 | ``` 43 | 44 | The next step is to check whether the `hi.nim` file indeed 45 | contains the program code. The geek will use the `cat` 46 | command once more. 47 | 48 | ```Nim 49 | › cat hi.nim 50 | import os, std/[Terminal] 51 | styledEcho "Hi, ", styleItalic, fgBlue, paramStr(1) 52 | ``` 53 | 54 | Finally, it is necessary to compile the program. This 55 | means that the geek will convert the `hi.nim` source 56 | file into an object file, which the machine understands 57 | well enough in order to carry out the instructions that 58 | send a message to the terminal. 59 | 60 | ```shell 61 | › nim c -o:hi.x --nimcache:xx hi.nim 62 | ``` 63 | 64 | The final step is to execute the object file to test 65 | whether it is functioning correctly. Since the Nim 66 | compiler reported that it generated code with *Success*, 67 | you can expect that the file `hi.x` is executable. Therefore, 68 | let us execute it: 69 | 70 | 71 | > › `./hi.x` Edward 72 | Hi, \textcolor{blue}{\it{Edward}} 73 | 74 | In chapter \ref{chap:tacit}, I will discuss knowledge. 75 | Then you will learn that knowledge can be explicit, 76 | tacit and shared. To discover how this can affect your 77 | study of the Nim programming language, let us see what 78 | Socrates said about knowledge. 79 | 80 | Calias had two sons. Of course, he spent a lot of money 81 | hiring teachers for his sons. So, Socrates asked him: 82 | *"Calias, if your sons were colts or calves, we could 83 | find and engage a trainer for them who would make them 84 | excel in their proper qualities, some horse breeder or 85 | farmer. Now since they are men, whom do you have in mind 86 | to coach them? Who is an expert in this kind of excellence, 87 | oratory and political debate? I think you must have given 88 | thought to this since you have sons. Is there such a person,"* 89 | Socrates asked, *"or is there not?"* *"For sure there is,"* 90 | Calias said. *"Who is he?"* Socrates asked, *"What is his 91 | name, where is he from? and what is his fee?"* Calias 92 | replied: *"The coach you are looking for, dear Socrates, 93 | is Evenus, a citizen from Paras. His fee is five minas."* 94 | 95 | At the time of Socrates, the all important human activity 96 | was political discussion, thus Evenus taught how to debate 97 | in assemblies and councils. Nowadays, people are more 98 | interested in computer programming, since these Weapons 99 | of Math Destruction, as Cathy O'Neil calls them, rule our 100 | lives. However, let us make an analogy. Let us suppose that 101 | you have two children, like Calias. If you wanted them to 102 | play the violin, instead of programming, you will start out 103 | buying a violin primer, such as Suzuki's Violin School, 104 | Volume 1, but you would not expect that your children 105 | learn music just by reading a book. Therefore, you will 106 | hire a music coach that will go to your house three 107 | times a week, in order to supervise your children's progress. 108 | If the coach insists in explaining only music theory, you 109 | will probably fire her, as you know one must practice a lot 110 | to become a proficient musician. 111 | 112 | You can use the same rules that apply to music for learning 113 | computer programming. Then you need a book, a coach and a 114 | lot of practice, if you want to learn how to write effective 115 | code. In consequence, it is a good idea that you really 116 | hire a major in computer science to guide your first steps 117 | in the computer world. The coach will explain the meaning of 118 | such terminologies as compiler, library, terminal, files, 119 | source editors and applications. Another thing that you can 120 | expect from the coach is that she will perform many activities 121 | on the computer for you to observe and repeat. 122 | 123 | 124 | # State machines 125 | My private library has many books in Ancient Greek, 126 | Latin, German, Esperanto, French, Japanese, Chinese, 127 | Guarany, Russian and other languages that I don't 128 | speak. Even so, I read the first two pages of these 129 | books, to know whether it was worthwhile to learn the 130 | language and read until the end. War and Peace is 131 | written in French and Russian, thus I read the French 132 | part of the first page and put the book back on the 133 | shelf, where it remained over many years as a feast for 134 | cockroaches that ate the glue on the cover. 135 | 136 | Keeping with my bad reading habits, I read only the first 137 | page of Wittgenstein's Tratactus Logico-Philosophicus, 138 | which has a title in Latin, but is written in German. 139 | Here is the first line of the Tratactus: 140 | 141 | 1. Die Welt ist alles, was der Fall ist. 142 | - 1.1. Die Welt ist die Gesamtheit der Tatsachen, nicht der Dinge. 143 | 144 | On the first line, an English speaker can recognize a 145 | lot of words, for instance, *Welt* must mean *World*, 146 | *alles* can be translated by *all* and *Fall* is *fall*. 147 | There was that Roman deity, Fortune, who decided the 148 | destiny of the World by throwing dice. It seems that 149 | Wittgenstein is saying that the World is all that the 150 | state of the Fortune's dice indicates that it is. 151 | 152 | On the second line, Wittgenstein is saying that the 153 | World is the totality of facts, not of things. Of course, 154 | things do exist, but the World is something beyond a 155 | set of things. What is the entity, which is beyond a mere 156 | collection of things? Let us read a little further to 157 | see if the philosopher sheds light on the subject. 158 | 159 | > Was der Fall ist, die Tatsache, ist das Bestehen von 160 | > Sachverhalten. 161 | 162 | David Pears translates the above line as 163 | 164 | > *What is a case -- a fact -- is the existence of 165 | > states of affairs.* 166 | 167 | In Latin, the word *STATUS* (Romans used to write only 168 | in uppercase letters) means the temporary attributes of 169 | a person or thing. What is an attribute? It is the 170 | position, station, place, posture, order, arrangement, 171 | condition, characteristic, aspect, feature, quality or 172 | trait that a thing can possess. An attribute has values, 173 | for instance, the attribute *color* can have values such 174 | as red, green, blue, yellow, etc. The *position* attribute 175 | can be given by the values of the Cartesian coordinates 176 | of the object. 177 | 178 | Computers are state machines, therefore they are good 179 | for modeling the world and its evolution through change 180 | of states. Computers themselves have registers, where they 181 | can store the values of the attributes that the machine 182 | represents. Computer programs abstract the registers into 183 | variables. Therefore, a program represents states by sets 184 | of values for its variables. To understand this point, let 185 | us consider a concrete problem. 186 | 187 | ```Nim 188 | # File: zeller.nim 189 | import os, strutils 190 | 191 | proc roman_order(m : int): int= 192 | let wm= (14 - m) div 12 193 | result= m + wm*12 - 2 194 | 195 | proc zr(y:int, m:int, day:int): int= 196 | let 197 | roman_month= roman_order(m) 198 | roman_year= y - (14 - m) div 12 199 | century= roman_year div 100 200 | decade= roman_year mod 100 201 | result= (day + (13*roman_month - 1) div 5 + 202 | decade + decade div 4 + 203 | century div 4 + 204 | century * -2) mod 7 205 | 206 | proc main () = 207 | let 208 | year = paramStr(1).parseInt 209 | month = paramStr(2).parseInt 210 | day = paramStr(3).parseInt 211 | echo zr(year, month, day) 212 | 213 | main() 214 | ``` 215 | 216 | (@zeller) Day of the week by Zeller's congruence 217 | 218 | You certainly know that the word `December` means 219 | the tenth month; it comes from the Latin word for 220 | ten, as attested in words such as: 221 | 222 | - decimal -- base ten. 223 | - decimeter -- tenth part of a meter. 224 | - decimate -- the killing of every tenth Roman soldier 225 | that performed badly in battle. 226 | 227 | October is named after the Latin numeral for *eighth*. 228 | In fact, the radical *Oct-* can be translated as *eight*, 229 | like in *octave*, *octal*, and *octagon*. One could 230 | continue with this exercise, placing September in the 231 | seventh, and November in the ninth position in the 232 | sequence of months. But everybody and his brother know 233 | that December is the twelfth, and that October is the 234 | tenth month of the year. Why did the Romans give 235 | misleading names to these months? 236 | 237 | Rome was purportedly founded by Romulus, who designed a 238 | calendar. March was the first month of the year in the 239 | Romulus calendar. In order to get acquainted with 240 | programming and the accompanying concepts, let us follow 241 | the Canadian programmer Nia Vardalos, while she explores 242 | the Nim programming language and calendar calculations. 243 | 244 | If Nia wants the order for a given month in this mythical 245 | calendar created by Romulus, she must subtract 2 from the 246 | order in the Gregorian calendar, which happens to be in 247 | current use in the Western World since it was instituted by 248 | Pope Gregory XIII in 1582. After the subtraction, September 249 | becomes the seventh month; October, the eight; November, 250 | the ninth and December, therefore, the tenth month. 251 | 252 | Farming and plunder were the main occupations of the Romans, 253 | and since winter is not the ideal season for either of these 254 | activities, the Romans did not care much for January and 255 | February. However, Sosigenes of Alexandria, at the request 256 | of Cleopatra and Julius Cæsar, designed the Julian calendar, 257 | where January and February, the two deep winter months, appear 258 | in positions 1 and 2 respectively. Therefore, a need for a 259 | formula that converts from the modern month numbering to the 260 | old sequence has arisen. In listing @zeller, this is done 261 | by the following algorithm: 262 | 263 | ```Nim 264 | 265 | proc roman_order(m : int): int= 266 | let wm= (14 - m) div 12 267 | result= m + wm*12 - 2 268 | 269 | ``` 270 | 271 | In the above formula, the variable `wm` will pick the value 1 272 | for months 1 and 2, and 0 for months from 3 to 12. In fact, 273 | for months from 3 to 12, `(14-m)` produces a number less 274 | than 12, and `(14-m) div 12` is equal to 0. Therefore, the 275 | state of the program for all months from March through December 276 | is given by `wm=0` and `result=m-2`. When the variable `wm=0`, 277 | the expression to calculate `result` is reduced from 278 | `result=m+wm×12-2` to `result=m-2`. This means that March 279 | will become the Roman month 1, and December will become the 280 | Roman month 10. 281 | 282 | The variable `wm` models the state of the world for the winter 283 | months. By the way, `wm` stands for *winter months*. Since 284 | January is month 1, and February is month 2 in the Gregorian 285 | calendar, `wm` is equal to 1 for these two winter months. In the 286 | case of January, the state is given by: `m=1`, `wm=1`, `result=11`. 287 | For February, the state becomes `m=2`, `wm=1`, `result=12`. 288 | 289 | 290 | The program of the listing @zeller calculates the day of the week 291 | through Zeller's congruence. In the procedure `zr(y, m, day)`, 292 | the let-statement binds values to the following state variables: 293 | `roman_month`, `roman_year`, `century` and `decade`. The listing 294 | @zeller shows that a state variable avoids recalculating values 295 | that appear more than once in a program. This is the case of 296 | `roman_year`, which appears four times in the `zr(y,m,day)` 297 | procedure. Besides this, by identifying important subexpressions 298 | with meaningful names, the program becomes clearer and cleaner. 299 | 300 | ## Compile and run 301 | 302 | After compiling the program of listing @zeller, one can calculate 303 | the day of the week as a number between 0 and 6, which correspond to 304 | Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and Saturday. 305 | For the time being, you do not need to worry about compilation 306 | and running a program on the terminal. This book dedicates a whole chapter to the use of a text terminal. In any case, here is an example 307 | of running the zeller congruence: 308 | 309 | ```Shell 310 | › nim c -o:zeller.x -d:release --hints:off --nimcache:xx zeller.nim 311 | CC: zeller.nim 312 | 313 | › ./zeller.x 2016 8 31 314 | 3 315 | 316 | ``` 317 | 318 | This means that August 31 of 2016 fell on a Wednesday. 319 | 320 | 321 | ## Sequence of statement 322 | In linguistics, a statement is a declarative sentence that 323 | describes a state or a state variable. In a program, one 324 | may need a sequence of statements to determine all state 325 | variables. In the programming language Nim, a sequence of 326 | statements is indicated by indentation, i.e., all statements 327 | that start at the same column belong to the same sequence. 328 | 329 | ## Procedures 330 | 331 | In computer programming, a procedure is a sequence of 332 | statements that isolates and determines a state. In Nim, 333 | the procedure can cause change of state by performing 334 | destructive assignment into variables introduced by the 335 | key word `var`, but there is no way of changing the 336 | value of variables introduced by the keyword `let`, such 337 | as the variables that appear in listing @zeller. 338 | 339 | ## How to repeat a sequence of statements 340 | 341 | During a programmer's life, he or she may need to repeat a 342 | sequence of statements to generate a succession of steps 343 | that will approach an end state or value. This repetition 344 | process is called iteration after the Latin word for path. 345 | 346 | Leonardo Bigollo Pisano, a.k.a. Fibonacci, was speculating 347 | on how fast the reproduction of rabbits would be. His breeding 348 | of rabbits has the interesting properties of mating at the 349 | age of one month, thus, at the end of her second month, the 350 | female produces a new pair of rabbits, a male and a female. 351 | Then she keeps producing two babies a month for one year. 352 | 353 | Fibonacci concluded that the number A~n~ of adult pairs 354 | in a given month is equal to the total number R~n-1~ 355 | of rabbits, both babies and adults, from the previous month. 356 | The clever fellow also perceived that, since each adult 357 | pair produces two babies a month, the number of baby 358 | pairs in a given month is the number of adult pairs from 359 | the previous month, which is the total number of rabbits from 360 | two months before. The total number of rabbit pairs, that 361 | is adults plus babies, in any given month is the sum of the 362 | pairs from the previous two months. 363 | 364 | ```Nim 365 | # File: iterfib.nim 366 | import os, strutils 367 | 368 | proc fib(n: int): int = 369 | var (r1, r2) = (2, 1) 370 | for i in 2..n: 371 | (r1, r2) = (r1+r2, r1) 372 | result= r1 373 | 374 | echo fib(paramStr(1).parseInt) 375 | ``` 376 | 377 | In the above program, the iteration is controlled by the 378 | for-loop, that repeats a change of state over generations 379 | from 2 through `n` of rabbits. Here is how the program is 380 | compiled and executed: 381 | 382 | ``` 383 | › nim c --nimcache:xx -o:rabbits.x -d:release --hints:off iterfib.nim 384 | › ./rabbits.x 5 385 | 13 386 | ``` 387 | 388 | ## Recursion 389 | Another way to discover the number of pairs in the previous 390 | generation is to sum the number of pairs from three generations 391 | ago to the number of pairs from two generations ago. This reasoning 392 | does nothing more than apply the rule of finding the total number 393 | of pairs in the present generation over the previous generation. 394 | Here is a program that uses this idea: 395 | 396 | ```Nim 397 | #› nim c --nimcache:xx -o:recfib.x -d:danger --hints:off recfib.nim 398 | import os, strutils 399 | proc fib(n: int): int = 400 | if n<2: result= n+1 401 | elif n<3: result= fib(n-1)+fib(n-2) 402 | else: result= fib(n-3)+fib(n-2)+fib(n-2) 403 | 404 | echo fib(paramStr(1).parseInt) 405 | ``` 406 | 407 | (@recfib) Recursive Fibonacci function 408 | 409 | The program of listing @recfib has many novelties. The first one is 410 | the conditional execution of a sequence of statements. For instance, `result=n+1` sets `result` to `n+1`, but only if the condition `n<2` 411 | is met. On the same token, `result=fib(n-1)+fib(n-2)` sets `result` 412 | to `fib(n-1)+fib(n-2)`, if `n<3`. 413 | 414 | However, the strangest feature of listing @recfib, is the 415 | use of the `fib` function in the definition of `fib`. In 416 | other words, the definition of `fib` calls itself. When such 417 | a thing happens, computer scientists say that the definition 418 | is recursive. 419 | 420 | Typically a recursive definition has two kinds of conditions, 421 | which in Nim are introduced by the if-statement: 422 | 423 | - Trivial conditions, which can be resolved using primitive operations. 424 | - General conditions, which can be broken down into simpler cases. 425 | 426 | In listing @recfib, the trivial condition is -- 427 | 428 | + `if n<2: result=n+1` 429 | 430 | The general conditions are introduced by the `elif` and `else` 431 | clauses for rewriting the expression `fib(n)` into one of 432 | the following expressions: 433 | 434 | ```Nim 435 | elif n<3: result= fib(n-1)+fib(n-2) 436 | else: result= fib(n-3)+fib(n-2)+fib(n-2) 437 | ``` 438 | 439 | Where each occurrence of a call to `fib` is closer to the trivial 440 | case than `fib(n)`, which was the original call. 441 | 442 | The mathematician Peano invented a very interesting axiomatic 443 | theory for natural numbers, that can be summarized thus: 444 | 445 | 1. Zero is a natural number. 446 | 2. Every natural number has a successor: The successor of 0 is 1, the 447 | successor of 1 is 2, the successor of 2 is 3, and so on. 448 | 3. If a property is true for zero and, after assuming that it is true for n, you prove that it is true for n+1, then it is true for any natural number. 449 | 450 | Did you get the idea? For this very idea can be applied to many other 451 | situations, even in programming a computer. 452 | 453 | Luciano Lima and Roger Alan think that it is very important to 454 | reproduce Peano's theory in the Latin original. However, I 455 | decided against meeting their demands for many reasons. In his 456 | book, Arithmetices Principia, Peano adopted a notation that is 457 | beyond the reach of most readers. Besides this, the 1889 edition 458 | by the Fratres Bocca has many typos that I need to fix before 459 | considering it for inclusion in this book. 460 | 461 | Sergio Teixeira and Stephanie Bourdieu said that my implementation 462 | of the Fibonacci procedure was not faithful to the 1228 edition of 463 | the Liber Abaci. I changed the algorithms as recommended by them, 464 | and also reproduced the fable of the 377 rabbits that appear in 465 | chapter 12 of Fibonacci's book. 466 | 467 | Readers who do not know Latin, or are not interested in Fibonacci's 468 | original work, can skip the rest of this chapter without any loss of 469 | important information. 470 | 471 | #### Liber Abaci -- Chapter XII: The Fable of the Rabbits {-#LiberAbaci} 472 | 473 | A certain man keeps one pair of rabbits in an enclosed place. 474 | This man wishes to know how many rabbits will be generated by 475 | the original pair in a period of one year. It is the nature of 476 | this breed of rabbits in a single month to bear another pair, 477 | and in the second month those born to bear also. 478 | 479 | Since the individuals of the first pair of rabbits are adults, 480 | in the first month, they will bear a pair of kittens, therefore 481 | the number of animals will double; so, there will be two pairs 482 | in one month. One of these, namely the first, bears in the 483 | second month, and thus there are in the second month 3 pairs; 484 | of these in one month two are pregnant, and in the third month 485 | 2 pairs of rabbits are born, and thus there are 5 pairs in the 486 | month; in this month 3 pairs are pregnant, and in the fourth 487 | month there are 8 pairs, of which 5 pairs bear another 5 pairs; 488 | these are added to the 8 pairs making 13 pairs in the fifth 489 | month; these 5 pairs that are born in this month do not mate in 490 | this month, but another 8 pairs are pregnant, and thus there are 491 | in the sixth month 21 pairs; to these are added the 13 pairs 492 | that are born in the seventh month; there will be 34 pairs in this 493 | month; to this are added the 21 pairs that are born in the eighth 494 | month; there will be 55 pairs in this month; to these are added 495 | the 34 pairs that are born in the ninth month; there will be 89 496 | pairs in this month; to these are added again the 55 pairs that 497 | are born in the tenth month; there will be 144 pairs in this month; 498 | to these are added again the 89 pairs that are born in the eleventh 499 | month; there will be 233 pairs in this month. 500 | 501 | To these are still added the 144 pairs that are born in the last 502 | month; there will be 377 pairs, and this many pairs are produced 503 | from the aforementioned pair of rabbits in the enclosed place at 504 | the end of the one year. 505 | 506 | You can indeed see in the margin how we operated, namely that we 507 | added the first number to the second, namely the 1 to the 2, and 508 | the second to the third, and the third to the fourth, and the 509 | fourth to the fifth, and thus one after another until we added the 510 | tenth to the eleventh, namely the 144 to the 233, and we had the 511 | total sum of rabbits, i.e. 377. Thus, you can systematically find 512 | the number of rabbits for an indeterminate number of months. 513 | 514 | #### Liber Abaci -- Capitulum XII: Fabula Cuniculorim {-#Liber} 515 | 516 | Quot paria coniculorum in uno anno ex uno pario germinentur? 517 | 518 | Quidam posuit unum par cuniculorum in quodam loco, qui erat 519 | undique pariete circundatus, ut sciret, quot ex eo paria 520 | germinarentur in uno anno: cum natura eorum sit per singulum 521 | mensem aliud par germinare; et in secundo mense ab eorum 522 | nativitate germinant. 523 | 524 | Quia suprascriptum par in primo mense germinat, duplicabis ipsum, 525 | erunt paria duo in uno mense. Ex quibus unum, silicet primum, 526 | in secundo mense geminat; et sic sunt in secundo mense paria 3; 527 | ex quibus in uno mense duo pregnantur; et geminantur in tercio 528 | mense paria 2 coniculorum; et sic sunt paria 5 in ipso mense; 529 | ex quibus in ipso pregnantur paria 3; et sunt in quarto mense 530 | paria 8; ex quibus paria 5 geminant alia paria 5: quibus additis 531 | cum pariis 8, faciunt paria 13 in quinto mense; ex quibus paria 5, 532 | que geminata fuerunt in ipso mense, non concipiunt in ipso mense, 533 | sed alia 8 paria pregnantur; et sic sunt in sexto mense paria 21; 534 | cum quibus additis parijs 13, que geminantur in septimo, erunt in 535 | ipso paria 34, cum quibus additis parijs 21, que geminantur in 536 | octavo mense, erunt in ipso paria 55; cum quibus additis parijs 34, 537 | que geminantur in nono mense, erunt in ipso paria 89; cum quibus 538 | additis rursum parijs 55, que geminantur in decimo, erunt in ipso 539 | paria 144; cum quibus additis rursum parijs 89, que geminantur in 540 | undecimo mense, erunt in ipso paria 233. Cum quibus etiam additis 541 | parijs 144, que geminantur in ultimo mense, erunt paria 377, 542 | et tot paria peperit suprascriptum par in prefato loco in capite 543 | unius anni. Potes enim videre in hac margine, qualiter hoc operati 544 | fuimus, scilicet quod iunximus primum numerum cum secundo, 545 | videlicet 1 cum 2; et secundum cum tercio; et tercium cum quarto; 546 | et quartum cum quinto, et sic deinceps, donec iunximus decimum 547 | cum undecimo, videlicet 144 cum 233; et habuimus suprascriptorum 548 | cuniculorum summam, videlicet 377; et sic posses facere per ordinem 549 | de infinitis numeris mensibus. 550 | 551 | The farmer of Fibonacci's fable starts with a pair of addult 552 | rabbits. Therefore, at the end of month 1, there were two pairs 553 | of rabbits. In the instant zero of the experiment there was 554 | only the original pair. Therefore, in listing @recfib, one has 555 | the following clause for `n<2`: 556 | 557 | ```Nim 558 | if n<2: result= n+1 559 | ``` 560 | 561 | Since in the most general clause Stephanie needed three 562 | generations of rabbits, she added a clause for the second 563 | month, to wit: 564 | 565 | ```Nim 566 | elif n<3: result= fib(n-1)+fib(n-2) 567 | ``` 568 | 569 | From the second month onward Stephanie can use the most 570 | general clause: 571 | 572 | ```Nim 573 | else: result= fib(n-3)+fib(n-2)+fib(n-2) 574 | ``` 575 | 576 | In general, recursive calculation of Fibonacci's function 577 | is very inefficient, and is often used in benchmarks. 578 | However, good compilers succeed to optimize the recursion 579 | away, therefore the algorithm given by listing @recfib is 580 | very fast. 581 | 582 | 583 | # Shell 584 | 585 | Nia, a young Greek woman, has an account on a well-known social 586 | network. She visits her friends' postings on a daily basis, and 587 | when she finds an interesting picture or video, she presses 588 | the *Like*-button. However, when she needs to discuss her upcoming 589 | holidays on the Saba Island with her Argentinian boyfriend, she uses 590 | the live chat box. After all, hitting buttons and icons offers only 591 | a very limited interaction tool, and does not produce a highly 592 | detailed level of information that is possible in a chat box. 593 | 594 | Using a chat service needs to be very easy and fun, otherwise 595 | teenagers would be doing something else. I am telling you this, 596 | because there are two ways of commanding the operating system (OS) 597 | that the computer uses to control mouse, keyboard, mass storage 598 | devices, etc. The first is called Graphical User Interface (GUI) 599 | and consists of moving a mouse and clicking over a menu option 600 | or an icon, such as the `Like` button. As previously mentioned, 601 | a GUI often does not generate adequate information for making a 602 | request to the OS. In addition, finding the right object to press 603 | can become difficult in a labyrinth of menu options. 604 | 605 | The other method of interacting with the computer is known as 606 | Shell, and is similar to a chat service. On a Shell interface, 607 | Nia issues instructions that the operating system (OS) answers 608 | by fulfilling the assigned tasks. The language that Nia uses 609 | to chat with the OS is called *bash*. Another option is *zsh*, 610 | but it is so similar to *bash* that it does not require a separate 611 | tutorial. Languages such as *bash* and *zsh* have commands to go 612 | through folders, browser files, create new directories, copy 613 | objects from one place to the other, configure the machine, 614 | install applications, change the permissions of a file, etc. 615 | When accessing the OS through a text-based terminal, a shell 616 | language is the main way of executing programs and doing work 617 | on a computer. 618 | 619 | In order not to scare off the feeble-minded, many operating 620 | systems hide access to the text terminal. In some distribution 621 | of Linux, you need to maintain the `Alt` key down, then press 622 | the `F2` key to open a dialog box, where you must type the 623 | name of the terminal you want to open. If you are really lucky, 624 | you may find the icon of the terminal on the tool bar. 625 | If the method for opening the text terminal is not so obvious, 626 | you should ask for help from a student majoring in Computer Science. 627 | 628 | ## The prompt {-#The} 629 | The shell prompt is where one types commands. The prompt 630 | has different aspects, depending on the configuration of 631 | the terminal. In Nia's machine, it looks something like 632 | this: 633 | 634 | ```Shell 635 | ~$ _ 636 | ``` 637 | 638 | Files are stored in folders. Typically, the prompt shows the 639 | folder where the user is currently working. The main duty of 640 | the operating system is to maintain the content of the mass 641 | storage devices in a tree structure of files and folders. 642 | Folders are also called *directories*, and like physical 643 | folders or cabinets, they organize files. A folder can be put 644 | inside another folder. In a given machine, there is a folder 645 | reserved for duties carried out by the administrator. This 646 | special folder is called `HOME` or personal directory. 647 | 648 | Now, let us learn a few commands to control the terminal 649 | and get things moving. 650 | 651 | ```bash 652 | nim/nimacros# cd ~ 653 | ~$ mkdir wrk 654 | ~$ cd wrk 655 | ~/wrk$ cat < hi.nim 656 | heredoc> import os 657 | heredoc> stdout.writeLine "Hello ", paramStr(1) 658 | heredoc> EOF 659 | ~/wrk$ ls 660 | hi.nim 661 | ~/wrk$ cat hi.nim 662 | import os 663 | stdout.writeLine "Hello ", paramStr(1) 664 | ``` 665 | 666 | The first command in the above dialog is `cd ~` that changes 667 | the prompt to the `HOME` folder, which is represented by a tilde. 668 | The second command, `mkdir wrk`, creates the `wrk` folder 669 | inside the `HOME` directory. The `cd wrk` statement puts 670 | the cursor prompt inside the newly created `wrk` directory. 671 | 672 | The `cat < hi.nim` command sends to the `hi.nim` file 673 | a text that terminates with the `EOF` token. The terminating 674 | token does not need to be `EOF`, in fact, you can choose anything 675 | to close the input. The `<` points to 678 | file `hi.nim`, which is the destination of the `cat` output. 679 | 680 | In general, people use `cat` for printing the contents of a file, 681 | exactly as Nia did when she issued the `cat hi.nim` command in 682 | the above example. However, I could not resist the idea of 683 | providing you with a more interesting use for the `cat` command. 684 | 685 | Finally, in the above example, the `ls` command lists the files, 686 | which are stored inside the `wrk` folder. The combination of `ls` 687 | and `cd` permits the browsing of the tree of files and folders, 688 | therefore one must learn how to use it well, which will be taught 689 | in the following pages. 690 | 691 | ### pwd {-#pwd} 692 | The `pwd` command informs the cursor prompt position in the 693 | file tree. A folder can be placed inside another folder. 694 | For example, in a Macintosh, the `HOME` folder is inside 695 | the `/Users` directory, therefore, if Nia issues the `pwd` 696 | command from her `HOME` folder, she obtains the result 697 | that is shown below. 698 | 699 | ```Shell 700 | ~$ set -k 701 | ~$ pwd # shows the current folder. 702 | Users/nia 703 | ``` 704 | 705 | One uses a path to identify a nest of folders. In a path, 706 | a sub-folder is separated from the parent folder by a slash 707 | bar. If one needs to know the path to the current folder, 708 | there is the `pwd` command. 709 | 710 | When Nia issues a command, she may add comments to it, 711 | so her boyfriend that does not know the Bourne-again 712 | shell (*bash*) can understand what is going on and learn 713 | something in the process. Just like in Nim, comments are 714 | prefixed with the `#` hash char, as you can see in the above 715 | chat. Therefore, when the computer sees a `#` hash char, 716 | it ignores everything to the end of the line. 717 | 718 | In the *Z shell* (*zsh*), it is necessary to use the `set -k` 719 | command to activate comments, but in the *bash* shell, comments 720 | are always active by default. 721 | 722 | 723 | ### mkdir wrk {-#mkdir} 724 | The command **mkdir wrk** creates a `wrk` folder inside 725 | the current directory, where `wrk` can be replaced with 726 | any other name. Therefore, if Nia issues the `mkdir wrk` 727 | command from her `HOME` directory, she creates a new 728 | folder with the `Users/nia/wrk` path. 729 | 730 | ### cd wrk {-#cd} 731 | One can use the `cd ` command to enter the named 732 | directory. The `cd ..` command takes Nia to the parent of the 733 | current directory. You also learned that `ch ~` sends the prompt 734 | to the `HOME` directory. Thanks to the `cd` command, one can 735 | navigate through the tree of folders and directories. 736 | 737 | ### Tab {-#Tab} 738 | If you want to go to a given directory, type part of the 739 | directory path, and then press *Tab*. The shell will complete the 740 | folder name for you. 741 | 742 | 743 | ### Home directory {-#Home} 744 | A `~` tilde represents the home directory. For instance, `cd ~` 745 | will send Nia to her personal folder. The `cd $HOME` has exactly 746 | the same effect. 747 | 748 | ```Shell 749 | ~/wrk$ ls 750 | hi.nim 751 | ~/wrk$ cd ~ 752 | ~$ cd wrk 753 | ~/wrk$ cd $HOME 754 | ~$ 755 | ``` 756 | 757 | ### echo and cat {-#echo} 758 | The `echo` command prints its arguments. Therefore, `echo $HOME` 759 | prints the contents of the `HOME` environment variable. Environment 760 | variables store the terminal configuration. For instance, the `HOME` 761 | variable contains the user's personal directory identifier. One 762 | needs to prefix environment variables with the `$` char to access 763 | their contents: 764 | 765 | ```Shell 766 | ~$ echo $HOME 767 | /Users/nia 768 | ``` 769 | 770 | The instruction `echo "import os, strutils" > fb.nim` creates 771 | a `fb.nim` file and writes the argument of the `echo` command 772 | there. If the file exists, this command supersedes it. 773 | 774 | The command `echo "# Fibonacci function" >> ifib.nim` appends a 775 | string to a text file. It does not erase the previous content 776 | of the `ifib.nim` file. Of course, you should replace the 777 | string or the file name, as necessity dictates. 778 | 779 | ```Shell 780 | ~$ cd wrk # transfer action to the wrk file 781 | ~/wrk$ echo 'import os, strutils\n' > ifib.nim 782 | ~/wrk$ { 783 | cursh> echo 'proc fib(n: int): int =' 784 | cursh> echo ' var (r1, r2) = (2, 1)' 785 | cursh> } >> ifib.nim 786 | ~/mwrk$ ls 787 | hi.nim ifib.nim 788 | ~/wrk$ cat ifib.nim 789 | import os, strutils 790 | 791 | proc fib(n: int): int = 792 | var (r1, r2) = (2, 1) 793 | ``` 794 | 795 | The above example shows that you can use braces to create 796 | a sequence of `echo` commands. The `cat ifib.nim` prints 797 | the contents of file `ifib.nim`, as you learned before. 798 | 799 | ### Extended example of cat {-#Extended} 800 | Below you will find an extended example of a chat between Nia 801 | and *zsh* with many examples of `cat` and `echo`. The `\n` 802 | directive in the string `import os, strutils\n` provokes a 803 | line break. Note that Nia replaced `EOF` with `EOT` just to 804 | show that it can be done. 805 | 806 | ```bash 807 | ~$ cd wrk # transfer action to the wrk file 808 | ~/wrk$ echo 'import os, strutils\n' > ifib.nim 809 | ~/wrk$ { 810 | cursh> echo 'proc fib(n: int): int =' 811 | cursh> echo ' var (r1, r2) = (2, 1)' 812 | cursh> } >> ifib.nim 813 | ~/wrk$ ls 814 | hi.nim ifib.nim 815 | ~/wrk$ cat <> ifib.nim 816 | heredoc> for i in 2..n: 817 | heredoc> (r1, r2) = (r1+r2, r1) 818 | heredoc> result= r1 819 | heredoc> 820 | heredoc> echo fib(paramStr(1).parseInt) 821 | heredoc> EOT 822 | ~/wrk$ cat ifib.nim 823 | import os, strutils 824 | 825 | proc fib(n: int): int = 826 | var (r1, r2) = (2, 1) 827 | for i in 2..n: 828 | (r1, r2) = (r1+r2, r1) 829 | result= r1 830 | 831 | echo fib(paramStr(1).parseInt) 832 | ``` 833 | 834 | ### ls {-#ls} 835 | By convention, a file name has two parts, the *id* and the 836 | *extension*. The id is separated from the extension by a dot. 837 | The `ls` command lists all files and sub-folders present in 838 | the current folder. The `ls *.nim` prints only files with 839 | the `.txt` extension. 840 | 841 | The `*.nim` pattern is called wild card. In a 842 | wild card, the `*` asterisk matches any sequence of chars, 843 | while the `?` interrogation mark matches a single char. 844 | The command `ls -lia *.nim` prints detailed information 845 | about the `.nim` files, like date of creation, size, etc. 846 | 847 | ```bash 848 | ~/wrk$ ls -lia *.nim 849 | 1291 -rw-r--r-- 1 ed staff 49 Nov 2 08:44 hi.nim 850 | 1292 -rw-r--r-- 1 ed staff 163 Nov 2 10:44 ifib.nim 851 | ``` 852 | 853 | Files starting with a dot are called hidden files, due to 854 | the fact that the `ls` command does not normally show them. 855 | All the same, the `ls -a` option includes the hidden files 856 | in the listing. 857 | 858 | In the preceding examples, the first character in each list 859 | entry is either a dash `(-)` or the letter `d`. A dash `(-)` 860 | indicates that the file is a regular file. The letter `d` 861 | indicates that the entry is a folder. A special file type 862 | that might appear in a `ls -la` command is the `symlink`. 863 | It begins with a lowercase `l`, and points to another 864 | location in the file system. Directly after the file 865 | classification comes the permissions, represented by the 866 | following letters: 867 | 868 | + `r` -- read permission. 869 | + `w` -- write permission. 870 | + `x` -- execute permission. 871 | 872 | ### cp {-#cp} 873 | The `cp ifib.nim fib.nim` makes a copy of a file. You can 874 | copy a whole directory with the `-rf` options, as shown 875 | below. 876 | 877 | ```zsh 878 | ~/wrk$ ls 879 | hi.nim ifib.nim 880 | ~/wrk$ cp ifib.nim fib.nim 881 | ~/wrk$ ls 882 | fib.nim hi.nim ifib.nim 883 | ~/wrk$ cd .. 884 | ~$ cp -rf wrk discard 885 | ~$ cd discard 886 | ~/discard$ ls 887 | fib.nim hi.nim ifib.nim 888 | ``` 889 | 890 | ### rm {-#rm} 891 | The `rm ifb.nim` command removes a file. The `-rf` option 892 | removes a whole folder. 893 | 894 | ```bash 895 | ~/discard$ ls 896 | fib.nim hi.nim ifib.nim 897 | ~/discard$ ls 898 | fib.nim hi.nim ifib.nim 899 | ~/discard$ rm ifib.nim 900 | ~/discard$ ls 901 | fib.nim hi.nim 902 | ~/discard$ cd .. 903 | ~$ rm -rf discard 904 | ~$ ls discard 905 | ls: discard: No such file or directory 906 | ``` 907 | 908 | ### mv {-#mv} 909 | The `mv wrk work` command changes the name of a file or 910 | folder, or even permits the moving of a file or folder 911 | to another location. By the way, the `cp bkp/ifib.nim .` 912 | copies the `ifib.nim` file to the current directory, 913 | which is represented by a `(.)` dot. You can use 914 | a `(.)` dot to represent the current directory with any 915 | shell command. 916 | 917 | ```zsh 918 | ~# mv wrk work 919 | ~$ cd work 920 | ~/work$ ls 921 | fib.nim hi.nim ifib.nim 922 | ~/work$ mkdir bkp 923 | ~/work$ mv ifib.nim bkp 924 | ~/work$ ls 925 | bkp fib.nim hi.nim 926 | ~/work$ ls bkp 927 | ifib.nim 928 | ~/work$ cp bkp/ifib.nim . 929 | ~/work$ ls 930 | bkp fib.nim hi.nim ifib.nim 931 | ``` 932 | 933 | ### Pen drive {-#Pen} 934 | In most Linux distributions, the pen drive is seen as 935 | a folder inside the `/media/nia/` directory, where you 936 | should replace `nia` with your user name. However, in 937 | the Macintosh, the pen drive appears at the `/Volume/` 938 | folder. The commands `cp`, `rm` and `ls` see the pen 939 | drive as a normal folder. 940 | 941 | ### Nim distribution package {-#Nim} 942 | 943 | The distribution package for a piece of software such 944 | as Nim is called archive, since it contains many files 945 | stored together in a compact way, that you will need to 946 | decompress and extract. 947 | 948 | A popular tool for decompressing an archive is `tar` that 949 | accepts files with extensions `.tar.xz`, `.bz2` or `tar.xz` 950 | depending on the method of compression. Extraction is 951 | performed by the `tar` application. 952 | 953 | You will learn in this book that it is impossible to write 954 | down all details of a craft. For becoming really proficient, 955 | you need practice. In any case, I will provide some guidance 956 | on the installation of the Nim compiler, but you will learn 957 | to get the thing done if you put in some efforts of your own. 958 | Ask for help from a computer science major, if you think 959 | that the task is above your station. 960 | 961 | You should search the Internet for the Nim language compiler 962 | and download it. Then, extract, build and install the 963 | distribution, as shown below. 964 | 965 | To protect you against malware attacks, one needs 966 | a password to write into the folders where critical 967 | applications are installed. The `sudo` tool will 968 | ask you for a password. If you type the correct password, 969 | the `install.sh` script will be granted the permission to 970 | install Nim in your computer. 971 | 972 | ```bash 973 | ~$ mkdir source 974 | ~$ cd source 975 | ~/source$ mv ~/Downloads/nim-x.y.z-os.tar.xz . 976 | ~/source$ tar xfJ nim-x.y.z-os.tar.xz 977 | ~/source$ cd nim-x.y.z 978 | source/nim-x.y.z$ ls *.sh 979 | build.sh deinstall.sh install.sh 980 | source/nim-x.y.z$ ./build.sh 981 | source/nim-x.y.z$ sudo ./install.sh /usr/local/bin 982 | ``` 983 | 984 | Finally, you should test the installation with a 985 | small program, as shown in the shell chat below. 986 | During the process of compilation, Nim creates 987 | auxiliary files in the folder indicated by the 988 | `--nimcache` option. I usually place these files 989 | in the `xx` folder, which I remove to liberate 990 | space in my machine. 991 | 992 | ```bash 993 | source/nim-1.0.3$ cd .. 994 | ~/source$ mkdir tests 995 | ~/source$ cd tests 996 | source/tests$ cat < hi.nim 997 | heredoc> import os 998 | heredoc> 999 | heredoc> stdout.writeLine "Hello ", paramStr(1) 1000 | heredoc> EOT 1001 | source/tests$ nim c -o:hi.x -d:release --hints:off --nimcache:xx hi.nim 1002 | CC: stdlib_io.nim 1003 | CC: stdlib_system.nim 1004 | CC: stdlib_posix.nim 1005 | CC: stdlib_times.nim 1006 | CC: stdlib_os.nim 1007 | CC: hi.nim 1008 | source/tests$ ./hi.x Ed 1009 | Hello Ed 1010 | source/tests$ ls 1011 | hi.nim hi.x xx 1012 | source/tests$ rm -rf xx 1013 | ``` 1014 | 1015 | # Emacs / lem 1016 | 1017 | You can create source files by using the `cat` command. 1018 | However, for serious work, you need a text editor such 1019 | as lem, which is a clone of Emacs written in the Common 1020 | Lisp programming language. I will not try to explain how 1021 | to install lem, since the procedure changes over time and 1022 | from machine to machine. Therefore, search the web for 1023 | adequate binaries and instruction on how to install the 1024 | thing on your computer. 1025 | 1026 | In the following cheat sheet for lem, `C-` is the `Ctrl` key, `M-` 1027 | denotes the `Alt` key, $\kappa$ can be any key, and `Spc` 1028 | represents the space bar. Thus, `C-`$\kappa$ means: Press and 1029 | release the `Ctrl` key and the $\kappa$ key simultaneously. 1030 | 1031 | + `C-s` -- search for a string of text. Press `C-s`, then type in 1032 | the text you want to find 1033 | + `C-s` again -- After the first occurrence, press `C-s` again 1034 | to find other text instances 1035 | + C-r -- reverse search 1036 | + `C-k` -- kill the text from the cursor, until the end of the line 1037 | + `C-h` -- backspace: erase the char before the cursor and move backwards 1038 | + `C-d` -- delete char under the cursor 1039 | + `C-Spc` then move the cursor -- select a region 1040 | + `M-w` -- save selected region in the kill ring 1041 | + `C-w` -- kill region, but save its contents in the kill ring 1042 | + `C-y` -- insert killed/saved region at current cursor position 1043 | + `C-g` -- cancel minibuffer reading 1044 | + `C-a` -- go to beginning of line 1045 | + `C-e` -- go to end of line 1046 | + `C-/` -- undo 1047 | + `INS` -- toggle overwrite mode 1048 | + `C-b` -- move back one character 1049 | + `C-f` -- move forward one character 1050 | + `C-n` -- move cursor to the next line 1051 | + `C-p` -- move cursor to the previous line 1052 | + ←↑↓→ -- the arrow keys also move the cursor 1053 | 1054 | ### Ctrl-x commands {-#Ctrl-x} 1055 | 1056 | 1057 | The convention `C-x C-κ` means that you should keep 1058 | the `Ctrl` key down and press `x` and `κ` in sequence. 1059 | You must try to issue the commands in a way that is 1060 | ergonomic and comfortable. Keep the `Ctrl` key pressed 1061 | with the index finger of the left hand, and use the 1062 | index finger of the right hand to press the keys `x` 1063 | and `κ`, one after the other. 1064 | 1065 | + `C-x C-f` -- open a file into a new buffer 1066 | + `C-x C-w` -- write file with new name 1067 | + `C-x C-s` -- save the current file 1068 | + `C-x C-c` -- exit the lem source editor 1069 | + `C-x C-i` -- insert file at current cursor position 1070 | 1071 | The `C-x ?` command -- describes a key stroke. Press 1072 | the `Ctrl` key and the `x` key at the same time, release 1073 | both keys, then press the `?` question mark. The one-line 1074 | buffer at the bottom of the page is called the minibuffer. 1075 | The command `C-x C-f` that finds a file reads the name of 1076 | the file from the minibuffer. This time, the minibuffer will 1077 | prompt you with the `describe-key` invitation. If you type 1078 | `C-x C-f`, the following description shows up: 1079 | 1080 | - `describe-key: C-x C-f find-file` 1081 | 1082 | The `C-x @` command -- pipes a shell instruction. 1083 | Keep the `Ctrl` key down, then press the `x` key. 1084 | Release both keys, then press the `@` key. The 1085 | lem editor will prompt for a shell command. Type 1086 | `ls`, for instance. It will open a temporary buffer 1087 | and show a list of file names. There are many commands 1088 | that read information from the minibuffer: 1089 | 1090 | + `C-x C-f` -- retrieves the file you want to open from the minibuffer 1091 | + `C-x C-s` -- reads the text sample you search for from the minibuffer 1092 | + `C-x C-i` -- in the minibuffer, type a file name to insert at current 1093 | cursor position 1094 | 1095 | 1096 | You must type `C-g`, whenever you want to cancel any 1097 | command that needs to read a complement from the minibuffer. 1098 | 1099 | ### Window commands {-#Window} 1100 | 1101 | One can have more than one window on the screen at any given moment. 1102 | In the list below, you will find commands that deal with this situation. 1103 | In commands of the form `C-x κ` -- keep the `Ctrl` key down and 1104 | press `x`, then release both keys and hit the `κ` key. 1105 | 1106 | + `C-x b` -- next buffer 1107 | + `C-x C-b` -- list buffers available 1108 | + `C-x k` -- kill current buffer 1109 | + `C-x 2` -- split window into cursor window and other window 1110 | + `C-x o` -- jump to the other window 1111 | + `C-x 1` -- close the other window 1112 | + `C-x 0` -- close the cursor window 1113 | 1114 | You can maintain many files open at the same time. I mean, when 1115 | you open a new file with the `C-x C-f` command, the buffer on 1116 | which you were working is not discarded, but remains in the 1117 | background. When you type `C-x b`, lem takes the cursor to the 1118 | minibuffer, where you can use the arrow keys to scroll and choose 1119 | the next buffer you want to edit. If you press `C-x C-b`, lem 1120 | provides a list of all buffers available, so you can choose one. 1121 | In this case, issue a `C-x o` command so that the cursor switches 1122 | to the buffer list window, from where you can choose the destination 1123 | buffer. 1124 | 1125 | ![](figs-prefix/minibuffer.jpg "Minibuffer"){width=250px} 1126 | 1127 | (@minibuffer) Fibonacci Function 1128 | 1129 | The figure above shows the editor. On the minibuffer you can 1130 | read the following message that was left there as the byproduct 1131 | of a `C-x C-s` save file command: 1132 | 1133 | > `Wrote /Users/ed/work/fib.nim` 1134 | 1135 | Let us test the editor. Call `lem` from the `work` directory 1136 | that you created previously: 1137 | 1138 | ```bash 1139 | ~$ cd work 1140 | ~/work$ ls 1141 | bkp hi.nim ifib.nim xx 1142 | ~/work$ lem fib.nim 1143 | ``` 1144 | 1145 | Type the code shown in figure @minibuffer, then exit the 1146 | editor with the `C-x C-c` command. Next, compile and run 1147 | the program, as shown below. 1148 | 1149 | ```bash 1150 | ~/work$ nim c -o:fib.x -d:release --nimcache:xx --hints:off fib.nim 1151 | ~/work$ ./fib.x 5 1152 | 13 1153 | ``` 1154 | 1155 | 1156 | ### Meta keys {-#Meta} 1157 | 1158 | To issue commands in the `M-κ` form, keep the `Alt` key 1159 | down and press the `κ` key. 1160 | 1161 | + `M-b` -- move back one word 1162 | + `M-f` -- move forward one word 1163 | + `M-g` -- go to the line given on the minibuffer 1164 | + `M->` -- go to the end of buffer 1165 | + `M-<` -- go to the beginning of buffer 1166 | 1167 | It is pretty hard to press the `M-<` command. You must 1168 | keep the `Alt` key down, then press the `Shft` and `<` 1169 | keys together. However, there is a `~/.lem/init.el` 1170 | initialization file where one can define new commands. 1171 | So, let's add the following commands to the `~/.lem/init.el` file: 1172 | 1173 | ```Lisp 1174 | ;; -*- lisp -*- 1175 | (in-package :lem) 1176 | 1177 | (define-key *global-keymap* "Escape" 'keyboard-quit) 1178 | 1179 | (define-key *global-keymap* "C-/" 'undo) 1180 | 1181 | (define-key *global-keymap* "M-p" 'move-to-beginning-of-buffer) 1182 | (define-key *global-keymap* "M-n" 'move-to-end-of-buffer) 1183 | ``` 1184 | Now, next time you enter lem, if you press `M-p`, you 1185 | will go to the beginning of the buffer. Likewise, if 1186 | you press `M-n`, the cursor will be sent to the end 1187 | of the buffer. 1188 | 1189 | ### Search {-#Search} 1190 | If you press `C-s`, the computer enters into search 1191 | mode. First, lem prompts you for the text snippet S 1192 | that you want it to find in the current buffer. While 1193 | you are still typing, the cursor jumps to the first 1194 | occurrence of the text snippet S. To repeat the search, 1195 | all you need to do is press `C-s` again. Finally, you 1196 | must type `C-r` to reverse the direction of the search. 1197 | 1198 | 1199 | ### Go to line {-#Go} 1200 | When you try to compile code containing errors, the compiler 1201 | usually reports the line number where the error occurred. 1202 | If you press `M-g`, lem prompts for a line number. As soon 1203 | as you type the number and press the `Enter` key, the cursor 1204 | jumps to the line where the error occurred. 1205 | 1206 | 1207 | ### Transport and Copy {-#Transport} 1208 | To transport a region from one place to another, Nia 1209 | presses `C-Spc` to start the selection process and 1210 | moves the cursor to select the region. Then she presses 1211 | `C-w` to kill her selection. Finally, she moves the 1212 | cursor to the insertion place, and presses the `C-y` 1213 | shortcut. 1214 | 1215 | To copy a region, Nia presses `C-Spc` and moves the 1216 | cursor to select the region. Then she presses `M-w` 1217 | to save the selection into the kill ring. Finally, 1218 | she takes the cursor to the destination where the 1219 | copy is to be inserted and issues the `C-y` command. 1220 | 1221 | 1222 | 1223 | # The Nim computer language 1224 | 1225 | ```Nim 1226 | # nim c -d:release --nimcache:lixo -o:rd.x rd.nim 1227 | import os, strutils, sequtils, sugar 1228 | proc avg(xs: seq[float]): float = 1229 | result= 0.0 1230 | var n= 0.0 1231 | for x in xs: 1232 | result= result + x 1233 | n= n+1.0 1234 | result= result/n 1235 | 1236 | proc main() = 1237 | if paramCount() < 1: quit("Usage: " & paramStr(0) & " ") 1238 | let s = readFile(paramStr(1)).splitWhitespace.map(x => x.parseFloat) 1239 | echo "Sum= ", s.foldl(a + b), " / Average= ", avg(s) 1240 | 1241 | main() 1242 | ``` 1243 | 1244 | (@readfile) Read and process a file 1245 | 1246 | Let us find out how many students graduate from 1247 | medical schools in California. The `grad.data` 1248 | file gives the number of graduates from each 1249 | school. The `rd.nim` program prints the addition 1250 | and the average. Here is how to compile and run 1251 | the program of listing @readfile: 1252 | 1253 | ``` 1254 | src> nim c -o:rd.x -d:release rd.nim # Compile 1255 | src> cat nums.data # Check the data 1256 | 190 45 23 34 89 96 78 1257 | 97 14 17 54 345 3 42 1258 | 1259 | src> ./rd.x nums.data # Run the program 1260 | Sum= 1127.0 / Average= 80.5 1261 | ``` 1262 | 1263 | \pagebreak 1264 | The predicate `paramCount() < 1` checks whether the file name is 1265 | present on the command line. If it is not, the program quits with 1266 | a request for the file name. In the snippet below, taken from 1267 | application @readfile, the `paramStr(0)` string contains the 1268 | application name. 1269 | 1270 | ``` 1271 | if paramCount() < 1: 1272 | quit("Usage: " & paramStr(0) & " ") 1273 | ``` 1274 | 1275 | The local variable `s` receives the result of a sequence of 1276 | operations concerning the file contents. 1277 | 1278 | The `readFile(paramStr(1))` operation reads the file whose 1279 | name is on the command line. The `nums.data` file contains 1280 | space separated numbers that `.splitWhitespace` parses and 1281 | produces a sequence of strings. 1282 | 1283 | Finally, `map(x => x.parseFloat)` transforms this sequence 1284 | into floating point numbers that `foldl(a+b)` adds together. 1285 | The `avg(xs: seq[float])` sums the floating point numbers 1286 | together into the `result` variable and calculates the length of 1287 | the sequence into `n`. The average is `result/n`. 1288 | 1289 | ![](figs-prefix/bugcerto.jpg "Voyage to the moon"){width=250px} 1290 | 1291 | The first computer was constructed by Konrad Zuse, 1292 | a German civil engineer, and his assistant, 1293 | Ms. Ursula Walk, née Hebekeuser. Ancient computers, 1294 | like those of Zuse and Walk, were based on relays. 1295 | These are bulky electrical devices, typically incorporating 1296 | an electromagnet, which is activated by a current 1297 | in one circuit to turn on or off another circuit. 1298 | Computers made of such a contrivance were enormous, 1299 | slow, and unreliable. Therefore, on September 9th, 1945, 1300 | a moth flew into one of the relays of the Harvard Mark II 1301 | computer and jammed it. From that time on, *bug* became 1302 | the standard word to indicate an error that prevents 1303 | a computer from working as intended. 1304 | 1305 | Due to bugs, compilers of languages like Nim and Haskell 1306 | frequently return error messages, instead of generating 1307 | code and running the corresponding programs. The Steel 1308 | Bank Common Lisp language does not interrupt code 1309 | generation when the compiler spots a bug, all the same 1310 | it does issue warnings that help find the problem before 1311 | the embarassment of failure is manifest on the client's 1312 | terminal. 1313 | 1314 | 1315 | 1316 | -------------------------------------------------------------------------------- /nimacros/chaps/nimdoc2.md: -------------------------------------------------------------------------------- 1 | # Comma separated values 2 | 3 | ```Nim 4 | # nim c -d:release -o:csv.x --nimcache:lixo csv.nim 5 | import os, strutils, sequtils, sugar 6 | 7 | proc main() = 8 | if paramCount() < 1: quit("Usage: " & paramStr(0) & "fname.data") 9 | let 10 | s = readFile(paramStr(1)).split(Whitespace+{','}) 11 | xs= s.filter(x => x.len > 0).map(x => x.parseFloat) 12 | echo "Average= ", xs.foldl(a+b)/float(xs.len) 13 | 14 | main() 15 | 16 | #[ 17 | Compile: nim c -d:release -o:csv.x --nimcache:lixo csv.nim 18 | src> cat csv.data 19 | 190, 180, 170, 160, 120, 100 20 | 100,90 21 | 22 | src> ./csv.x csv.data 23 | Average= 138.75 24 | ]# 25 | ``` 26 | 27 | The above program calculates the average of comma separated values. 28 | Everything that comes between `#[` and `]#` is a comment. In the 29 | listing above, the comments provide an example of how to compile and 30 | use the program. Text that comes after `#` and the end of a line 31 | is also a comment. This second kind of comment is very common in 32 | shell commands. The `split(Whitespace+{','})` operation splits a 33 | string with values that can be separated by any combination of chars 34 | that belong to the `Whitespace+{','}` set. Since `split` produces 35 | empty `""` strings, the program applies `filter(x => x.len > 0)` 36 | to the result, in order to eliminate zero-length strings from 37 | the sequence. 38 | 39 | ## Iterators 40 | 41 | ```Nim 42 | # nim c -d:release -o:ird.x --nimcache:lixo ird.nim 43 | import os, strutils 44 | 45 | iterator valid[T](a: seq[T]): T= 46 | for x in a: 47 | if x.len != 0: yield x 48 | 49 | proc avg(xs: seq[string]): float = 50 | result= 0.0 51 | var n= 0.0 52 | for x in valid(xs): 53 | n= n+1 54 | result= result+x.parseFloat 55 | result= result/n 56 | 57 | proc main() = 58 | if paramCount() < 1: quit("Usage: " & paramStr(0) & " fname") 59 | let 60 | s = readFile(paramStr(1)).split(Whitespace+{','}) 61 | echo avg(s) 62 | 63 | main() 64 | 65 | #[ 66 | src> nim c -o:ird.x -d:release --nimcache:./lixo ird.nim 67 | src> ./ird.x csv.data 68 | 138.75 69 | ]# 70 | ``` 71 | 72 | In the procedure that reads a file and splits it 73 | into an `int` sequence, the split function 74 | generates empty strings at the end of the file 75 | and possibly at the end of each line as well. 76 | Therefore, I designed an iterator that feeds 77 | a `for-loop` with valid strings that can be 78 | parsed into floats, which one can use to calculate 79 | the average of a sequence of values. 80 | 81 | In Nim, iterators are as easy to design as normal 82 | functions. In fact, iterators are functions that 83 | produce values more than once. They are defined like 84 | procedures, but the keyword *iterator* replaces 85 | the keyword *proc* that defines procedures. 86 | Another difference between iterators and functions is 87 | that an iterator uses the keyword *yield*, instead of 88 | the keyword *return* to produce a value. In general, 89 | iterators are used to feed a `for-loop` with a sequence 90 | of values. After yielding a value, the iterator can 91 | resume the computation to produce the next value. In 92 | the example, the iterator `valid` yields a sequence of 93 | strings that can be parsed to produce floating point 94 | numbers. 95 | 96 | -------------------------------------------------------------------------------- /nimacros/figs-prefix/bugcerto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/bugcerto.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/bugcerto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/bugcerto.png -------------------------------------------------------------------------------- /nimacros/figs-prefix/minibuffer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/minibuffer.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/neatsum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/neatsum.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/piggy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/piggy.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/readyforaction.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/readyforaction.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/stackshark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/stackshark.jpg -------------------------------------------------------------------------------- /nimacros/figs-prefix/webpage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/figs-prefix/webpage.jpg -------------------------------------------------------------------------------- /nimacros/nimdoc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Nim programming language 3 | author: 4 | - Victor Della-Vos 5 | - Proofreading -- Lecale, Marcus 6 | - Illustrations -- Priscila 7 | rights: MIT License 8 | language: en-US 9 | tags: [book] 10 | fontsize: 12pt 11 | numbersections: true 12 | header-includes: 13 | \usepackage[utf8]{inputenc} 14 | \usepackage[T1]{fontenc} 15 | \usepackage{textalpha} 16 | \usepackage{tikz} 17 | \usepackage{upquote} 18 | \usepackage{wrapfig} 19 | \usepackage{graphicx} 20 | \usepackage{amsmath} 21 | \usepackage{circuitikz} 22 | \usepackage{color} 23 | comment: 24 | pandoc -s nimdoc.md --syntax-definition nimrod.xml -o nimdoc.pdf 25 | --- 26 | 27 | -------------------------------------------------------------------------------- /nimacros/nimdoc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimacros/nimdoc.pdf -------------------------------------------------------------------------------- /nimacros/nimrod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 27 | 28 | 29 | 30 | const 31 | export 32 | import 33 | include 34 | lambda 35 | let 36 | var 37 | 38 | 39 | 40 | assert 41 | asm 42 | atomic 43 | block 44 | break 45 | case 46 | cast 47 | compiles 48 | continue 49 | declared 50 | declaredinscope 51 | defined 52 | discard 53 | do 54 | echo 55 | elif 56 | else 57 | end 58 | except 59 | finally 60 | for 61 | from 62 | if 63 | mixin 64 | bind 65 | new 66 | raise 67 | return 68 | sizeof 69 | try 70 | when 71 | while 72 | quit 73 | using 74 | yield 75 | 76 | 77 | 78 | addr 79 | and 80 | as 81 | div 82 | in 83 | is 84 | isnot 85 | mod 86 | not 87 | notin 88 | of 89 | or 90 | shl 91 | shr 92 | xor 93 | 94 | 95 | 96 | array 97 | bool 98 | byte 99 | cchar 100 | cdouble 101 | char 102 | cfloat 103 | cint 104 | clong 105 | cshort 106 | cstring 107 | cuint 108 | distinct 109 | expr 110 | float 111 | float32 112 | float64 113 | generic 114 | int 115 | int8 116 | int16 117 | int32 118 | int64 119 | interface 120 | openarray 121 | pointer 122 | set 123 | seq 124 | stmt 125 | string 126 | tuple 127 | typedesc 128 | uint 129 | uint8 130 | uint16 131 | uint32 132 | uint64 133 | varargs 134 | void 135 | untyped 136 | typed 137 | 138 | 139 | 140 | out 141 | ptr 142 | ref 143 | shared 144 | static 145 | 146 | 147 | 148 | false 149 | inf 150 | nil 151 | true 152 | on 153 | off 154 | 155 | 156 | 157 | result 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | -------------------------------------------------------------------------------- /nimacros/src/akavelIter.nim: -------------------------------------------------------------------------------- 1 | # make APP=akavelIter 2 | import macros, strutils, os 3 | 4 | macro iter(cmd: untyped, stmts: untyped): untyped = 5 | expectKind(cmd, nnkInfix) 6 | expectKind(cmd[0], nnKIdent) 7 | doAssert cmd[0].strVal == "->" 8 | expectKind(cmd[2], nnkIdent) 9 | expectKind(stmts, nnkStmtList) 10 | let (ix, rng) = (cmd[2], cmd[1]) 11 | result = nnkStmtList.newTree(nnkForStmt.newTree(ix, rng, stmts)) 12 | 13 | iter 2..paramStr(1).parseInt -> j: 14 | echo j, "- Give me some beer" 15 | 16 | -------------------------------------------------------------------------------- /nimacros/src/akavelLoop.nim: -------------------------------------------------------------------------------- 1 | # make APP=akavelLoop 2 | import macros, strutils, os 3 | 4 | macro iter(cmd: untyped, sts: untyped): untyped = 5 | # Checking syntax of command 6 | expectKind(cmd, nnkCommand) 7 | doAssert cmd.len == 2 8 | expectKind(cmd[1], nnkInfix) 9 | for i in 0..2: expectKind(cmd[1][i], nnkIdent) 10 | doAssert cmd[1][0].strVal == "->" 11 | doAssert cmd[1][1].strVal == "times" 12 | expectKind(sts, nnkStmtList) 13 | let 14 | rng = cmd[0] 15 | ix = cmd[1][2] 16 | result = nnkStmtList.newTree(nnkForStmt.newTree(ix, rng, sts)) 17 | 18 | iter (3..paramStr(1).parseInt) times -> j: 19 | echo j, "- Give me some beer" 20 | echo "Now" 21 | 22 | -------------------------------------------------------------------------------- /nimacros/src/bin/README.md: -------------------------------------------------------------------------------- 1 | # Binaries 2 | As customary, binaries generated from the 3 | `src` directory through the build.nims folder 4 | will be placed here. 5 | 6 | -------------------------------------------------------------------------------- /nimacros/src/build.nims: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S nim --hints:off 2 | mode = ScriptMode.Silent 3 | from os import `/` 4 | if paramCount() > 2 and fileExists(paramStr(3) & ".nim"): 5 | let 6 | app = paramStr(3) 7 | src = app & ".nim" 8 | exe = app & ".x" 9 | c = "nim c --hints:off --nimcache:xx -d:danger -o:" 10 | 11 | exec c & exe & " " & src 12 | echo c & exe & " " & src 13 | mvFile exe, "bin" / exe 14 | else: echo "Usage: ./build.nims " 15 | 16 | -------------------------------------------------------------------------------- /nimacros/src/cmd.nim: -------------------------------------------------------------------------------- 1 | import commandeer 2 | 3 | commandline: 4 | option texto, string, "string", "s", "Fibonacci" 5 | argument inteiro, int 6 | 7 | echo ("O inteiro= ", inteiro) 8 | echo ("A string= ", texto) 9 | 10 | proc fib(n: int) : int= 11 | if n < 2: 1 12 | else: fib(n-1)+fib(n-2) 13 | 14 | echo texto, " ", inteiro, "=", fib(inteiro) 15 | 16 | -------------------------------------------------------------------------------- /nimacros/src/commentest.nim: -------------------------------------------------------------------------------- 1 | # Single-line comment 2 | 3 | #[ 4 | Multiline comment 5 | Second line 6 | Third line 7 | ]# 8 | echo "Spanish letter: ñ" 9 | -------------------------------------------------------------------------------- /nimacros/src/cons.nim: -------------------------------------------------------------------------------- 1 | import os, strutils, macros 2 | 3 | type 4 | SExprKind = enum 5 | IntScalar, FloatScalar, St, Sym, consp 6 | SExpr = ref object 7 | case kind: SExprKind 8 | of IntScalar: intVal: int 9 | of FloatScalar: floatVal: float 10 | of Sym: symb: string 11 | of St: str: string 12 | of consp: car, cdr: SExpr 13 | 14 | template mI(a:int): SExpr= 15 | SExpr(kind: IntScalar, intVal: a) 16 | 17 | template sy(s: string): SExpr= 18 | SExpr(kind: Sym, symb: `s`) 19 | 20 | template mS(s:string): SExpr= 21 | SExpr(kind: St, str: s) 22 | 23 | template car(s:SExpr) : SExpr= 24 | if s == nil: s 25 | else: s.car 26 | 27 | template cdr(s:SExpr) : Sexpr= 28 | if s == nil: s 29 | else: s.cdr 30 | 31 | template cons(x:SExpr, y:SExpr) : SExpr= 32 | SExpr(kind: consp, car: x, cdr: y) 33 | 34 | proc `$`*(se: SExpr): string = 35 | case se.kind 36 | of IntScalar: result= $se.intVal 37 | of FloatScalar: result = $se.floatVal 38 | of ST: result = '"' & se.str & '"' 39 | of Sym: result = se.symb 40 | of consp: 41 | result.add("(") 42 | var r = se 43 | if r != nil: 44 | result.add($r.car) 45 | r= r.cdr 46 | while r != nil: 47 | result.add(indent($r.car, 2)) 48 | r= r.cdr 49 | result.add(")") 50 | 51 | let plus{.compileTime.}= "+".sy 52 | 53 | proc walkAST(e:SExpr): NimNode = 54 | case e.kind: 55 | of Sym: return newIdentNode e.symb 56 | of IntScalar: return newLit e.intVal 57 | of consp: 58 | if car(e) == plus: 59 | var callTree = nnkCall.newTree() 60 | callTree.add newIdentNode"+" 61 | callTree.add e.cdr.car.walkAST 62 | callTree.add e.cdr.cdr.car.walkAST 63 | return callTree 64 | else: return newLit "Erro" 65 | 66 | macro def(id:untyped, x:untyped, y:untyped): untyped= 67 | let ix= x.strVal.sy 68 | let iy= y.strVal.sy 69 | let body= cons(plus, cons(ix, cons(iy, nil))).walkAST 70 | quote do: 71 | proc `id`(`x`: int, `y`: int): int= 72 | `body` 73 | 74 | def(sum, cx, cy) 75 | 76 | proc main() = 77 | echo cons("hi".sy, cons(cons("world".sy, nil), nil)) 78 | echo sum(paramStr(1).parseInt, 8) 79 | 80 | main() 81 | 82 | -------------------------------------------------------------------------------- /nimacros/src/consp.nim: -------------------------------------------------------------------------------- 1 | # Library: consp.nim 2 | import strutils, macros 3 | type 4 | SExprKind = enum Intp, Floatp, St, Sym, consp 5 | SExpr = ref object 6 | case kind: SExprKind 7 | of Intp: intVal: int 8 | of Floatp: floatVal: float 9 | of Sym: symb: string 10 | of St: str: string 11 | of consp: h, t: SExpr 12 | 13 | template mI*(a:int): SExpr= SExpr(kind: Intp, intVal: a) 14 | template sy*(s: string): SExpr= SExpr(kind: Sym, symb: `s`) 15 | template car*(s:SExpr): SExpr= s.h 16 | template cdr*(s:SExpr): Sexpr= s.t 17 | template cons*(x:SExpr, y:SExpr): SExpr= SExpr(kind: consp, h: x, t: y) 18 | 19 | proc `$`*(se: SExpr): string = 20 | case se.kind 21 | of Intp: result= $se.intVal 22 | of Floatp: result = $se.floatVal 23 | of ST: result = '"' & se.str & '"' 24 | of Sym: result = se.symb 25 | of consp: 26 | result.add("(") 27 | var (r, ind) = (se, 0) 28 | while r != nil: 29 | result.add(indent($r.car, ind)) 30 | (r, ind)= (r.cdr, 1) 31 | result.add(")") 32 | 33 | let plus*{.compileTime.}= "+".sy 34 | proc walkAST*(e:SExpr): NimNode = 35 | case e.kind: 36 | of Sym: return newIdentNode e.symb 37 | of Intp: return newLit e.intVal 38 | of consp: 39 | if car(e) == plus: 40 | return nnkCall.newTree( newIdentNode"+", 41 | e.cdr.car.walkAST, 42 | e.cdr.cdr.car.walkAST) 43 | else: return newLit "Erro" 44 | 45 | -------------------------------------------------------------------------------- /nimacros/src/consproc.nim: -------------------------------------------------------------------------------- 1 | import os, strutils, macros 2 | 3 | type 4 | SExprKind = enum 5 | IntScalar, FloatScalar, St, Sym, consp 6 | SExpr = ref object 7 | case kind: SExprKind 8 | of IntScalar: intVal: int 9 | of FloatScalar: floatVal: float 10 | of Sym: symbol: string 11 | of St: str: string 12 | of consp: car, cdr: SExpr 13 | 14 | proc mI(a:int): SExpr= 15 | result= SExpr(kind: IntScalar, intVal: a) 16 | 17 | proc sy(s:string): SExpr= 18 | result= SExpr(kind: Sym, symbol: s) 19 | 20 | proc mS(s:string): SExpr= 21 | result= SExpr(kind: St, str: s) 22 | 23 | proc car(s:SExpr) : SExpr= 24 | if s == nil: s 25 | else: s.car 26 | 27 | proc cdr(s:SExpr) : Sexpr= 28 | if s == nil: s 29 | else: s.cdr 30 | 31 | proc cons(x:SExpr, y:SExpr) : SExpr= 32 | result= SExpr(kind: consp, car: x, cdr: y) 33 | 34 | proc `$`*(se: SExpr): string = 35 | case se.kind 36 | of IntScalar: result= $se.intVal 37 | of FloatScalar: result = $se.floatVal 38 | of ST: result = '"' & se.str & '"' 39 | of Sym: result = se.symbol 40 | of consp: 41 | result.add("(") 42 | var r = se 43 | if r != nil: 44 | result.add($r.car) 45 | r= r.cdr 46 | while r != nil: 47 | result.add(indent($r.car, 2)) 48 | r= r.cdr 49 | result.add(")") 50 | 51 | 52 | proc walkAST(e:SExpr): NimNode = 53 | case e.kind: 54 | of Sym: return newIdentNode e.symbol 55 | of IntScalar: return newLit e.intVal 56 | else: return newLit "Erro" 57 | 58 | let ii{.compileTime.} = "i".sy.walkAST 59 | 60 | macro iter(jj: untyped, c1: untyped, c2: untyped, stm: untyped): untyped= 61 | let 62 | jx= jj.strVal 63 | p= int(c1.intVal) 64 | result= nnkStmtList.newTree( 65 | nnkForStmt.newTree(jx.sy.walkAST, 66 | infix(p.mI.walkAST, "..", c2), stm)) 67 | 68 | proc main() = 69 | iter(j, 2, paramStr(1).parseInt): 70 | echo j, cons(sy("hi"), nil) 71 | 72 | main() 73 | 74 | -------------------------------------------------------------------------------- /nimacros/src/consrpn.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release -o:rpn.x --nimcache:lixo rpn.nim 2 | import os, strutils 3 | 4 | type LL= ref object of RootObj 5 | car: float 6 | cdr: LL 7 | 8 | template car(a:untyped) : untyped= 9 | if a == nil: quit("Empty stack") 10 | else: a.car 11 | 12 | template cons(x: untyped, y: untyped) : untyped= 13 | LL(car: x, cdr: y) 14 | 15 | proc eval(x: string, s: var LL)= 16 | try: 17 | s= x.strip.parseFloat.cons: s 18 | except: 19 | case x: 20 | of "+": 21 | s= ((car s) + (car s.cdr)).cons: s.cdr.cdr 22 | of "x": 23 | s= cons (car s) * (car s.cdr): s.cdr.cdr 24 | of "/": 25 | s= cons (car s.cdr) / (car s): s.cdr.cdr 26 | of "-": 27 | s= cons (car s.cdr) - (car s): s.cdr.cdr 28 | of "neg": 29 | s= cons -(car s): s.cdr 30 | else: quit("Error in eval") 31 | 32 | var stk: LL = nil 33 | for i in 1 .. paramCount(): eval(paramStr(i), stk) 34 | while stk != nil: 35 | echo stk.car 36 | stk= stk.cdr 37 | 38 | -------------------------------------------------------------------------------- /nimacros/src/costumary.nim: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | 4 | type 5 | LL= ref object of RootObj 6 | h: Unit 7 | t: LL 8 | UnitKind= enum 9 | mi, km, nm 10 | Unit = ref object 11 | case kind: UnitKind 12 | of mi: miVal: float 13 | of km: kmVal: float 14 | of nm: nmVal: float 15 | 16 | proc `$`*(x:Unit): string= 17 | case x.kind 18 | of mi: $x.miVal & "mi" 19 | of km: $x.kmVal & "km" 20 | of nm: $x.nmVal 21 | 22 | template car(a:untyped) : untyped= 23 | if a == nil: quit("Empty stack") 24 | else: a.h 25 | 26 | template `>>` (a,b:untyped): untyped= LL(h: a, t: b) 27 | 28 | proc eval(x: string, s: var LL)= 29 | try: s= Unit(kind: nm, nmVal: x.strip.parseFloat) >> s 30 | except: 31 | case x: 32 | of "km": 33 | if (car s).kind == nm: 34 | (car s).kind= km 35 | elif (car s).kind == km: 36 | echo (car s).kmVal, "km" 37 | else: 38 | s= Unit(kind: km, kmVal: s.h.miVal * 1.609) >> s.t 39 | of "mi": 40 | if (car s).kind == nm: 41 | (car s).kind= mi 42 | elif (car s).kind == mi: 43 | echo (car s).miVal, "mi" 44 | else: 45 | s= Unit(kind: mi, miVal: s.h.kmVal / 1.609) >> s.t 46 | else: quit("Error in eval") 47 | 48 | var s= "" 49 | var stk: LL= nil 50 | var stack: LL= nil 51 | 52 | stdout.write "> " 53 | while stdin.readline(s) and s != "quit": 54 | for x in s.splitWhitespace: 55 | eval(x, stk) 56 | stack= stk 57 | while stack != nil: 58 | echo stack.h 59 | stack= stack.t 60 | stdout.write "> " 61 | 62 | -------------------------------------------------------------------------------- /nimacros/src/counter.nim: -------------------------------------------------------------------------------- 1 | # Based on idea of Vindaar 2 | import macros, strutils, os 3 | 4 | proc parseArgs(cmd: NimNode): (NimNode, NimNode) = 5 | doAssert cmd.len == 2 6 | expectKind(cmd[0], nnkIdent) 7 | result = (cmd[0], # leave cmd[0] as is, has to be valid integer expr 8 | cmd[1]) # identifier to use for loop 9 | 10 | macro cnt(cmd: untyped, stmts: untyped): untyped = 11 | expectKind(cmd, nnkCommand) 12 | expectKind(stmts, nnkStmtList) 13 | let (iterVar, toIdx) = parseArgs(cmd) 14 | result = quote do: 15 | for `iterVar` in 1..`toIdx`: 16 | `stmts` 17 | echo result.repr 18 | 19 | cnt j paramStr(1).parseInt: 20 | echo j, "- Give me some beer" 21 | 22 | -------------------------------------------------------------------------------- /nimacros/src/csv.data: -------------------------------------------------------------------------------- 1 | 190, 180, 170, 160, 120, 100 2 | 100,90 3 | 4 | -------------------------------------------------------------------------------- /nimacros/src/csv.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release -o:csv.x --nimcache:lixo csv.nim 2 | import os, strutils, sequtils, sugar 3 | 4 | proc main() = 5 | if paramCount() < 1: quit("Usage: " & paramStr(0) & "fname.data") 6 | let 7 | s = readFile(paramStr(1)).split(Whitespace+{','}) 8 | xs= s.filter(x => x.len > 0).map(x => x.parseFloat) 9 | echo "Average= ", xs.foldl(a+b)/float(xs.len) 10 | 11 | main() 12 | -------------------------------------------------------------------------------- /nimacros/src/excrd.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release -o:xrd.x rd.nim 2 | 3 | import os, strutils, sequtils, sugar 4 | 5 | proc main() = 6 | proc avg(xs: seq[string]): float= 7 | var sm, n = 0.0 8 | for i, x in xs: 9 | try: 10 | sm = sm + x.strip.parseFloat 11 | n= n + 1.0 12 | except: discard 13 | finally: result= sm/n 14 | 15 | if paramCount() < 1: quit("Usage: " & paramStr(0) & " filename") 16 | let xs = readFile(paramStr(1)).split(Whitespace+{','}) 17 | echo avg(xs) 18 | 19 | main() 20 | 21 | -------------------------------------------------------------------------------- /nimacros/src/handsexpr.nim: -------------------------------------------------------------------------------- 1 | import os, strutils, macros 2 | 3 | type 4 | Node = ref object 5 | hd: SExpr 6 | tl: SExpr 7 | SExprKind = enum 8 | isINT, isFLT, isSTR, isID, isKw, isLST 9 | SExpr = ref object 10 | case kind: SExprKind 11 | of isINT: iVal: int 12 | of isFLT: fVal: float 13 | of isSTR: sVal: string 14 | of isID: id: string 15 | of isKW: kwd: string 16 | of isLST: cns: Node 17 | 18 | proc mI(a:int): SExpr= 19 | result= SExpr(kind: isINT, iVal: a) 20 | 21 | proc mId(s:string): SExpr= 22 | result= SExpr(kind: isID, id: s) 23 | 24 | proc mK(s:string): SExpr= 25 | result= SExpr(kind: isKW, kwd: s) 26 | 27 | proc car(s:SExpr) : SExpr= 28 | if s == nil: s 29 | else: s.cns.hd 30 | 31 | proc cdr(s:SExpr) : Sexpr= 32 | if s == nil: s 33 | else: s.cns.tl 34 | 35 | proc cons(x:SExpr, y:SExpr) : SExpr= 36 | result= SExpr(kind: isLST, cns: Node(hd: x, tl: y)) 37 | 38 | proc `$`*(se: SExpr): string = 39 | case se.kind 40 | of isINT: result= $se.iVal 41 | of isFLT: result = $se.fVal 42 | of isSTR: result = '"' & se.sVal & '"' 43 | of isID: result = se.id 44 | of isKW: result = se.kwd 45 | of isLST: 46 | result.add("(") 47 | var r = se 48 | if r != nil: 49 | result.add($r.cns.hd) 50 | r= r.cns.tl 51 | while r != nil: 52 | result.add(indent($r.cns.hd, 2)) 53 | r= r.cns.tl 54 | result.add(")") 55 | 56 | # result = nnkStmtList.newTree(nnkForStmt.newTree(ix, rng, stmts)) 57 | 58 | macro prs(s: string) : untyped= 59 | result = parseExpr(s.strVal) 60 | 61 | macro typecheck(x: typed): untyped= 62 | echo "The type is: ", repr x.getType() 63 | result = newStmtList() 64 | 65 | template stringParseTest(x) = typecheck prs x 66 | 67 | var xs: SExpr= nil 68 | 69 | let iFor = mK "for" 70 | 71 | let progn = mK "progn" 72 | 73 | let ixx= mId "ixx" 74 | 75 | macro iter(i, c1, c2, stm: untyped): untyped= 76 | result= nnkStmtList.newTree( 77 | nnkForStmt.newTree( 78 | i, infix(c1, "..", c2), stm)) 79 | 80 | proc main() = 81 | var xs= cons(mI 42, cons(cons(iFor, nil), nil)) 82 | echo xs 83 | echo "for-in-list= ", car(car (cdr xs)) 84 | echo "iFor== for-in-list? ", iFor == car(car (cdr xs)) 85 | echo ixx 86 | iter(i, 1,2,): 87 | echo i 88 | stringParseTest("cons(mI 42, nil)") 89 | 90 | main() 91 | 92 | -------------------------------------------------------------------------------- /nimacros/src/hello.nim: -------------------------------------------------------------------------------- 1 | echo "Hello" 2 | 3 | 4 | -------------------------------------------------------------------------------- /nimacros/src/hello.nims: -------------------------------------------------------------------------------- 1 | let src=system.paramStr(2) 2 | let flags= ".x --nimcache:xx --hints:off -d:danger " 3 | exec "nim c -o:" & src & flags & src & ".nim" 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /nimacros/src/indentest.nim: -------------------------------------------------------------------------------- 1 | echo (5 + 2 | 5) 3 | -------------------------------------------------------------------------------- /nimacros/src/infixLoop.nim: -------------------------------------------------------------------------------- 1 | # infixLoop.nim 2 | import macros, strutils, os 3 | 4 | proc parseArgs(cmd: NimNode): (NimNode, NimNode) = 5 | expectKind(cmd[0], nnkIdent) 6 | expectKind(cmd[1], nnkIdent) 7 | doAssert cmd[0].strVal == "++=" 8 | result = (cmd[1], cmd[2]) 9 | 10 | macro rpt(cmd: untyped, stmts: untyped): untyped = 11 | expectKind(cmd, nnkInfix) 12 | expectKind(stmts, nnkStmtList) 13 | let (iterVar, toIdx) = parseArgs(cmd) 14 | result = quote do: 15 | for `iterVar` in 1..`toIdx`: 16 | `stmts` 17 | 18 | rpt j ++= paramStr(1).parseInt: 19 | echo j, "- Give me some beer" 20 | 21 | -------------------------------------------------------------------------------- /nimacros/src/ird.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release -o:ird.x --nimcache:lixo ird.nim 2 | import os, strutils 3 | 4 | iterator valid[T](a: seq[T]): T= 5 | for x in a: 6 | if x.len != 0: yield x 7 | 8 | proc avg(xs: seq[string]): float = 9 | result= 0.0 10 | var n= 0.0 11 | for x in valid(xs): 12 | n= n+1 13 | result= result+x.parseFloat 14 | result= result/n 15 | 16 | proc main() = 17 | if paramCount() < 1: quit("Usage: " & paramStr(0) & " fname") 18 | let 19 | s = readFile(paramStr(1)).split(Whitespace+{','}) 20 | echo avg(s) 21 | 22 | main() 23 | -------------------------------------------------------------------------------- /nimacros/src/iterfib.nim: -------------------------------------------------------------------------------- 1 | # File: iterfib.nim 2 | import os, strutils 3 | 4 | proc fib(n: int): int = 5 | var (r1, r2) = (2, 1) 6 | for i in 2..n: 7 | (r1, r2) = (r1+r2, r1) 8 | result= r1 9 | 10 | echo fib(paramStr(1).parseInt) 11 | 12 | # A one line comment 13 | -------------------------------------------------------------------------------- /nimacros/src/lispmacros.nim: -------------------------------------------------------------------------------- 1 | # nim c -o:cons.x -d:danger --hints:off --nimcache:xx lispmacros.nim 2 | import os, strutils, macros, consp 3 | 4 | macro def(id:untyped, x:untyped, y:untyped): untyped= 5 | let ix= x.strVal.sy 6 | let iy= y.strVal.sy 7 | let body= cons(plus, cons(ix, cons(iy, nil))).walkAST 8 | quote do: 9 | proc `id`(`x`: int, `y`: int): int= 10 | `body` 11 | 12 | def(sum, cx, cy) 13 | 14 | echo cons("hi".sy, cons(42.mI, cons(cons("world".sy, nil), nil))) 15 | echo sum(paramStr(1).parseInt, 8) 16 | 17 | -------------------------------------------------------------------------------- /nimacros/src/nums.data: -------------------------------------------------------------------------------- 1 | 190 45 23 34 89 96 78 2 | 97 14 17 54 345 3 42 3 | 4 | -------------------------------------------------------------------------------- /nimacros/src/powertwo.nim: -------------------------------------------------------------------------------- 1 | 2 | template optMul{`*`(a,2)}(a: int): int= 3 | let x = a 4 | x+x 5 | 6 | template canonMul{`*`(a,b)}(a: int{lit}, b: int): int= 7 | b * a 8 | 9 | var x: int 10 | for i in 1 .. 1_000_000_000: 11 | x += 2 * i 12 | echo x 13 | 14 | -------------------------------------------------------------------------------- /nimacros/src/quoteLoop.nim: -------------------------------------------------------------------------------- 1 | # make APP=quoteLoop 2 | import macros, strutils, os 3 | 4 | macro magicWord(statments: untyped): untyped = 5 | ## Designed by Steve Kellock 6 | result = statments 7 | for st in statments: 8 | for node in st: 9 | if node.kind == nnkStrLit: 10 | node.strVal = node.strVal & ", Please." 11 | 12 | macro rpt(ix: untyped, cnt: int, statements: untyped)= 13 | quote do: 14 | for `ix` in 1..`cnt`: 15 | `statements` 16 | 17 | rpt j, paramStr(1).parseInt : 18 | magicWord: 19 | echo j, "- Give me some beer" 20 | echo "Now" 21 | -------------------------------------------------------------------------------- /nimacros/src/rd.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release --nimcache:lixo -o:rd.x rd.nim 2 | import os, strutils, sequtils, sugar 3 | proc avg(xs: seq[float]): float = 4 | result= 0.0 5 | var n= 0.0 6 | for x in xs: 7 | result= result + x 8 | n= n+1.0 9 | result= result/n 10 | 11 | proc main() = 12 | if paramCount() < 1: quit("Usage: " & paramStr(0) & " ") 13 | let s = readFile(paramStr(1)).splitWhitespace.map(x => x.parseFloat) 14 | echo "Sum= ", s.foldl(a + b), " / Average= ", avg(s) 15 | 16 | main() 17 | -------------------------------------------------------------------------------- /nimacros/src/rd.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def avg(xs: list[float]) -> float: 4 | result= 0.0 5 | n= 0.0 6 | for x in xs: 7 | result= result + x 8 | n= n+1.0 9 | return result/n 10 | 11 | numbers = [] 12 | 13 | def rdfile(file_name:str) -> list[float]: 14 | numbers= [] 15 | with open(file_name) as fp: 16 | #Iterate through each line 17 | for line in fp: 18 | numbers.extend( 19 | [float(item) 20 | for item in line.split() 21 | ]) 22 | return numbers 23 | 24 | def main(): 25 | print('Average= ', avg(rdfile(sys.argv[1]))) 26 | 27 | main() 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /nimacros/src/rdline.nim: -------------------------------------------------------------------------------- 1 | 2 | proc main()= 3 | let name= readLine(stdin) 4 | echo "Hello ", name 5 | 6 | main() 7 | 8 | -------------------------------------------------------------------------------- /nimacros/src/rdwrt.nim: -------------------------------------------------------------------------------- 1 | import os, strutils, math 2 | 3 | type LL= ref object of RootObj 4 | car: float 5 | cdr: LL 6 | 7 | template car(a:untyped) : untyped= 8 | if a == nil: quit("Empty stack") 9 | else: a.car 10 | 11 | template `>>` (a,b:untyped): untyped= LL(car: a, cdr: b) 12 | 13 | proc eval(x: string, s: var LL)= 14 | try: s= x.strip.parseFloat >> s 15 | except: 16 | case x: 17 | of "+": s= (car s) + (car s.cdr) >> s.cdr.cdr 18 | of "x": s= (car s) * (car s.cdr) >> s.cdr.cdr 19 | of "/": s= (car s.cdr) / (car s) >> s.cdr.cdr 20 | of "-": s= (car s.cdr) - (car s) >> s.cdr.cdr 21 | of "expt": s= pow(s.cdr.car, s.car) >> s.cdr.cdr 22 | of "fv": s= pow(1.0 + s.cdr.car/100, s.car) * 23 | s.cdr.cdr.car >> s.cdr.cdr.cdr 24 | of "neg": s= -(car s) >> s.cdr 25 | else: quit("Error in eval") 26 | 27 | var s= "" 28 | var stk: LL= nil 29 | var stack: LL= nil 30 | 31 | stdout.write "> " 32 | while stdin.readline(s) and s != "quit": 33 | for x in s.splitWhitespace: 34 | eval(x, stk) 35 | stack= stk 36 | while stack != nil: 37 | echo stack.car 38 | stack= stack.cdr 39 | stdout.write "> " 40 | 41 | -------------------------------------------------------------------------------- /nimacros/src/recfib.nim: -------------------------------------------------------------------------------- 1 | #› nim c --nimcache:xx -o:recfib.x -d:danger --hints:off recfib.nim 2 | import os, strutils 3 | proc fib(n: int): int = 4 | if n<2: result= n+1 5 | elif n<3: result= fib(n-1)+fib(n-2) 6 | else: result= fib(n-3)+fib(n-2)+fib(n-2) 7 | 8 | echo fib(paramStr(1).parseInt) 9 | 10 | -------------------------------------------------------------------------------- /nimacros/src/rep.lisp: -------------------------------------------------------------------------------- 1 | (defmacro itr(ixx c1xx c2xx &rest body) 2 | `(loop for ,ixx from ,c1xx to ,c2xx do 3 | (format t "~a- " ,ixx) ,@body)) 4 | 5 | (itr i 0 3 (format t "Hello, world~%") 6 | (format t "End~%")) 7 | 8 | 9 | -------------------------------------------------------------------------------- /nimacros/src/rep.nim: -------------------------------------------------------------------------------- 1 | # make APP=rep 2 | # Based on a model by Juan Carlos Paco 3 | import macros 4 | 5 | macro iter(i:untyped, c1:untyped, 6 | c2:untyped, stm:untyped): untyped = 7 | result = newNimNode(nnkStmtList) # creates an empty result 8 | var for_loop= 9 | newNimNode(nnkForStmt) # creates a for-loop 10 | for_loop.add(i) # adds index `i` to the for-loop 11 | 12 | var rng = # creates a range 13 | newNimNode(nnkInfix).add(ident("..")).add(c1,c2) 14 | for_loop.add(rng) # inserts the range into for_loop 15 | let spc= newLit("- ") # Creates a space string Lit 16 | var wrt = newCall(ident("write"),ident("stdout"), i, spc) 17 | var stmList= newNimNode(nnkStmtList) 18 | stmList.add(wrt) 19 | for s in stm: stmList.add(s) 20 | for_loop.add(stmList) 21 | result.add(for_loop) # insert for_loop into result 22 | 23 | iter(i, 0, 3): 24 | echo "Hello, world." 25 | echo "End" 26 | 27 | 28 | -------------------------------------------------------------------------------- /nimacros/src/rpn.nim: -------------------------------------------------------------------------------- 1 | # nim c -d:release -o:rpn.x --nimcache:lixo rpn.nim 2 | import os, strutils 3 | 4 | type LL= ref object of RootObj 5 | car: float 6 | cdr: LL 7 | 8 | template car(a:untyped) : untyped= 9 | if a == nil: quit("Empty stack") 10 | else: a.car 11 | 12 | template `>>` (a,b:untyped): untyped= LL(car: a, cdr: b) 13 | 14 | proc eval(x: string, s: var LL)= 15 | try: s= x.strip.parseFloat >> s 16 | except: 17 | case x: 18 | of "+": s= (car s) + (car s.cdr) >> s.cdr.cdr 19 | of "x": s= (car s) * (car s.cdr) >> s.cdr.cdr 20 | of "/": s= (car s.cdr) / (car s) >> s.cdr.cdr 21 | of "-": s= (car s.cdr) - (car s) >> s.cdr.cdr 22 | of "neg": s= -(car s) >> s.cdr 23 | else: quit("Error in eval") 24 | 25 | var stk: LL = nil 26 | for i in 1 .. paramCount(): eval(paramStr(i), stk) 27 | while stk != nil: 28 | echo stk.car 29 | stk= stk.cdr 30 | 31 | -------------------------------------------------------------------------------- /nimacros/src/sexpr.nim: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | type 4 | Node[T]= ref object 5 | head: SExpr 6 | tail: Node[T] 7 | SExprKind = enum 8 | isINT, isFLT, isSTR, isID, isLST 9 | SExpr = ref object 10 | case kind: SExprKind 11 | of isINT: iVal: int 12 | of isFLT: fVal: float 13 | of isSTR: sVal: string 14 | of isID: id: string 15 | of isLST: xs: Node[SExpr] 16 | 17 | proc cons[T](a:T, n: Node[T]): Node[T] = 18 | var 19 | res = Node[T](head: a, tail: n) 20 | return res 21 | 22 | proc mkList(n: int): SExpr = 23 | result = SExpr(kind: isLST) 24 | result.xs = nil 25 | for i in 0..n: 26 | result.xs = 27 | cons(SExpr(kind: isINT, iVal: i), result.xs) 28 | 29 | proc `$`*(se: SExpr): string = 30 | case se.kind 31 | of isINT: result= $se.iVal 32 | of isFLT: result = $se.fVal 33 | of isSTR: result = '"' & se.sVal & '"' 34 | of isID: result = se.id 35 | of isLST: 36 | result.add("(") 37 | var 38 | r = se.xs 39 | if r != nil: 40 | result.add($r.head) 41 | r= r.tail 42 | while r != nil: 43 | result.add(indent($r.head, 2)) 44 | r= r.tail 45 | result.add(")") 46 | 47 | proc soma(xs: SExpr): int= 48 | var 49 | r = 0 50 | s = xs.xs 51 | while s != nil: 52 | r = r + s.head.iVal 53 | s = s.tail 54 | return r 55 | 56 | proc main() = 57 | if paramCount() < 1: 58 | quit("Usage: x-sexpr.x 1000") 59 | let 60 | n = parseInt(paramStr(1)) 61 | echo mkList(3) 62 | var 63 | som = 0 64 | xs = mkList(1000000) 65 | for i in 1 .. n: 66 | som = som + soma(xs) 67 | echo "Soma= ", som 68 | 69 | main() 70 | 71 | -------------------------------------------------------------------------------- /nimacros/src/slowPowertwo.nim: -------------------------------------------------------------------------------- 1 | 2 | var x= 0 3 | for i in 1 .. 1_000_000_000: 4 | x += 2 * i 5 | echo x 6 | 7 | -------------------------------------------------------------------------------- /nimacros/src/sugarLoop.nim: -------------------------------------------------------------------------------- 1 | # Based on idea of Vindaar 2 | import macros, strutils, os 3 | 4 | proc parseArgs(cmd: NimNode): (NimNode, NimNode) = 5 | doAssert cmd.len == 2 6 | expectKind(cmd[1], nnkInfix) 7 | expectKind(cmd[1][0], nnkIdent) 8 | expectKind(cmd[1][1], nnkIdent) 9 | expectKind(cmd[1][2], nnkIdent) 10 | doAssert cmd[1][0].strVal == "->" 11 | doAssert cmd[1][1].strVal == "times" 12 | result = (cmd[0], # leave cmd[0] as is, has to be valid integer expr 13 | cmd[1][2]) # identifier to use for loop 14 | 15 | macro rpt(cmd: untyped, stmts: untyped): untyped = 16 | expectKind(cmd, nnkCommand) 17 | expectKind(stmts, nnkStmtList) 18 | let (toIdx, iterVar) = parseArgs(cmd) 19 | result = quote do: 20 | for `iterVar` in 1..`toIdx`: 21 | `stmts` 22 | echo result.repr 23 | 24 | rpt paramStr(1).parseInt times -> j: 25 | echo j, "- Give me some beer" 26 | echo "Now" 27 | 28 | -------------------------------------------------------------------------------- /nimacros/src/twodates.nim: -------------------------------------------------------------------------------- 1 | import times 2 | let auj = now() 3 | let deb = parse("04-09-2019", "dd-MM-yyyy") 4 | echo (auj - deb).inDays 5 | 6 | -------------------------------------------------------------------------------- /nimacros/src/units.nim: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | type 4 | LL= ref object of RootObj 5 | h: U 6 | t: LL 7 | UnitKind= enum mi= "mi", km= "km", nm= "nm" 8 | U = ref object 9 | case kind: UnitKind 10 | of mi, km, nm: fVal: float 11 | 12 | template car(a:untyped) : untyped= 13 | if a == nil: quit("Empty stack") else: a.h 14 | 15 | template psh(knd: untyped, v: untyped, s: untyped) = 16 | s = LL(h: U(kind: knd, fVal: v), t: s.t) 17 | 18 | proc eval(x: string, s: var LL)= 19 | try: s= LL(h: U(kind: nm, fVal: x.strip.parseFloat), t: s) 20 | except: 21 | case x : 22 | of "km": 23 | if (car s).kind == nm: (car s).kind= km 24 | elif (car s).kind == mi: psh(km, s.h.fVal * 1.609, s) 25 | of "mi": 26 | if (car s).kind == nm: (car s).kind= mi 27 | elif (car s).kind == km: psh(mi, s.h.fVal / 1.609, s) 28 | else: echo "?" 29 | 30 | var s= "" 31 | var stk: LL= nil 32 | var stack: LL= nil 33 | stdout.write "> " 34 | while stdin.readline(s) and s != "q": 35 | for x in s.splitWhitespace: eval(x, stk) 36 | stack= stk 37 | while stack != nil: 38 | echo stack.h.fVal, stack.h.kind 39 | stack= stack.t 40 | stdout.write "> " 41 | 42 | -------------------------------------------------------------------------------- /nimacros/src/zeller.nim: -------------------------------------------------------------------------------- 1 | import os, strutils 2 | 3 | proc roman_order(m : int): int= 4 | let wm= (14 - m) div 12 5 | result= m + wm*12 - 2 6 | 7 | proc zr(y:int, m:int, day:int): int= 8 | let 9 | roman_month= roman_order(m) 10 | roman_year= y - (14 - m) div 12 11 | century= roman_year div 100 12 | decade= roman_year mod 100 13 | result= (day + (13*roman_month - 1) div 5 + 14 | decade + decade div 4 + 15 | century div 4 + 16 | century * -2) mod 7 17 | 18 | proc main () = 19 | let 20 | year = paramStr(1).parseInt 21 | month = paramStr(2).parseInt 22 | day = paramStr(3).parseInt 23 | echo zr(year, month, day) 24 | 25 | main() 26 | 27 | -------------------------------------------------------------------------------- /nimdoc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FemtoEmacs/nimacros/f7ee7c360eb68cb3c3af01a0533472b13c25a754/nimdoc.pdf --------------------------------------------------------------------------------