├── 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 |
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 |
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 |
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 |
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 | {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 | {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
--------------------------------------------------------------------------------