├── 00-nim-basics.adoc ├── 01-installation.adoc ├── 02-variables.adoc ├── 03-types.adoc ├── 04-control-flow.adoc ├── 05-loops.adoc ├── 06-containers.adoc ├── 07-procedures.adoc ├── 08-modules.adoc ├── 09-user-input.adoc ├── 10-conclusion.adoc ├── LICENSE.txt ├── Makefile ├── README.md ├── code ├── assign.nim ├── break.nim ├── callProcs.nim ├── case.nim ├── chars.nim ├── continue.nim ├── conversion.nim ├── elif.nim ├── else.nim ├── filterOdds.nim ├── floats.nim ├── for1.nim ├── for2.nim ├── for3.nim ├── helloworld.nim ├── if.nim ├── indexing.nim ├── integers.nim ├── interaction1.nim ├── interaction2.nim ├── interaction3.nim ├── logicalOperators.nim ├── maths.nim ├── multipleCase.nim ├── numbers.txt ├── people.txt ├── readFromFile.nim ├── readFromFile2.nim ├── relationalOperators.nim ├── result.nim ├── seq.nim ├── stringConcat.nim ├── strings.nim ├── stringutils.nim ├── tuples.nim ├── ufcs.nim └── while.nim ├── index.html ├── nim-basics.epub ├── nim-basics.pdf ├── rouge-github.css ├── rouge-gruvbox.css └── styles ├── epub-cover.png ├── epub.css ├── pdf-theme.yml └── style.css /00-nim-basics.adoc: -------------------------------------------------------------------------------- 1 | = Nim basics 2 | :source-highlighter: rouge 3 | :rouge-style: gruvbox 4 | :source-language: nim 5 | :source-dir: code 6 | :pdf-themesdir: styles 7 | :pdf-theme: pdf 8 | :pdf-folio-placement: physical 9 | :front-cover-image: image:styles/epub-cover.png[Front Cover,1050,1600] 10 | :icons: font 11 | :stylesheet: styles/style.css 12 | :linkcss: 13 | :doctype: book 14 | :toc: left 15 | 16 | 17 | 18 | https://nim-lang.org/[Nim] is a relatively new programming language which allows users to write easy-to-read high-performance code. 19 | But if you are reading this Nim tutorial, the chances are that you already know about Nim. 20 | 21 | The tutorial is available both https://narimiran.github.io/nim-basics/[online] and as a https://github.com/narimiran/nim-basics/raw/master/nim-basics.epub[book in epub format]. 22 | 23 | This is a work-in-progress: if you spot any errors and/or you have an idea how to make this tutorial better, please report it to the https://github.com/narimiran/nim-basics/issues[issue tracker]. 24 | 25 | 26 | 27 | 28 | == Who is this for? 29 | 30 | * People with no or minimal previous programming experience 31 | * People with some programming experience in other programming languages 32 | * People who want to explore Nim for the first time, starting from scratch 33 | 34 | 35 | == Who is this _not_ for? 36 | 37 | * People with lots of programming experience: other, more advanced, tutorials might suit you better. 38 | See https://nim-lang.org/docs/tut1.html[Official Tutorial] or https://nim-by-example.github.io/[Nim by Example]. 39 | * People experienced in Nim (feel free to help make this tutorial better) 40 | 41 | 42 | == How to use this tutorial? 43 | 44 | The aim of this tutorial is to give you the basics of programming and the Nim syntax so you can have an easier time following other tutorials and/or explore further by yourself. 45 | 46 | Instead of just reading what is written, it would be the best if you try the stuff by yourself, modify the examples, think of some examples of your own, and be curious in general. 47 | The exercises at the end of some chapters should be non-negotiable -- don't skip them. 48 | 49 | If you need additional help understanding some parts of the tutorial or with the exercises, you can always ask for help on the https://forum.nim-lang.org/[Nim forum], the https://gitter.im/nim-lang/Nim[Nim Gitter channel], their https://discordapp.com/invite/ezDFDw2[Discord server], or Nim's IRC channel on freenode, #nim. 50 | 51 | 52 | include::01-installation.adoc[] 53 | 54 | include::02-variables.adoc[] 55 | 56 | include::03-types.adoc[] 57 | 58 | include::04-control-flow.adoc[] 59 | 60 | include::05-loops.adoc[] 61 | 62 | include::06-containers.adoc[] 63 | 64 | include::07-procedures.adoc[] 65 | 66 | include::08-modules.adoc[] 67 | 68 | include::09-user-input.adoc[] 69 | 70 | include::10-conclusion.adoc[] 71 | 72 | 73 | {nbsp} 74 | 75 | {nbsp} 76 | 77 | ''' 78 | 79 | {nbsp} 80 | 81 | The source files are available https://github.com/narimiran/nim-basics[on Github]. 82 | -------------------------------------------------------------------------------- /01-installation.adoc: -------------------------------------------------------------------------------- 1 | = Installation 2 | 3 | 4 | 5 | == Installing Nim 6 | 7 | Nim has ready made distributions for all three major operating systems and there are several options when it comes to installing Nim. 8 | 9 | You can follow https://nim-lang.org/install.html[the official installation procedure] to install the latest stable version, or you can use a tool called https://github.com/nim-lang/choosenim[choosenim] which enables you to easily switch between the stable and the latest development version if you're interested in the latest features and bugfixes. 10 | 11 | Whichever way you choose, just follow the installation procedure explained at each link and Nim should be installed. 12 | We will check that the installation went well in a coming chapter. 13 | 14 | If you're using Linux, there is a high probability that your distribution has Nim in the package manager. 15 | If you are installing it that way, make sure it's the most recent version (see the website for what is the latest version), otherwise install via one of two methods mentioned above. 16 | 17 | In this tutorial we will use the stable version. 18 | Originally, this tutorial was written for Nim 0.19 (released in September 2018), and it should work for any newer version, including Nim 1.x and Nim 2.x. 19 | 20 | 21 | 22 | 23 | == Installing additional tools 24 | 25 | You can write Nim code in any text editor, and then compile and run it from the terminal. 26 | If you want syntax highlighting and code completion there are plugins for popular code editors which provide these features. 27 | 28 | Most of Nim users prefer the https://code.visualstudio.com/[VS Code] editor, with the https://marketplace.visualstudio.com/items?itemName=NimLang.nimlang[Nim extension] which provides syntax highlighting and code completion, and the https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner[Code Runner extension] for quick compiling and running. 29 | 30 | The author personally uses https://neovim.io/[NeoVim] editor, with https://github.com/alaviss/nim.nvim[this plugin] which provides additional features like syntax highlighting and code completion. 31 | 32 | If you're using other code editors, see https://github.com/nim-lang/Nim/wiki/Editor-Support[the wiki] for available editor support. 33 | 34 | 35 | 36 | 37 | == Testing the installation 38 | 39 | To check if the installation was successful, we will write a program which is traditionally used as an introductory example: https://en.wikipedia.org/wiki/%22Hello,_World!%22_program[Hello World]. 40 | 41 | Printing (as in: displaying on the screen; not on a paper with a printer) the phrase `Hello World!` in Nim is straightforward and it doesn't require any boilerplate code. 42 | 43 | In a new text file called e.g. `helloworld.nim` we need to write just one line of code: 44 | 45 | [source] 46 | .helloworld.nim 47 | ---- 48 | include::code/helloworld.nim[] 49 | ---- 50 | 51 | NOTE: The phrase you want to print must follow the `echo` command and must be enclosed in double-quotes (`"`). 52 | 53 | First we need to compile our program, and then run it to see if it works as expected. 54 | 55 | Open your terminal in the same directory where your file is (on Linux you can get "Open Terminal here" if you right-click the directory in your file manager, on Windows you should use Shift + right-click to get the menu option for opening the command line). 56 | 57 | We compile our program by typing in the terminal: 58 | [source, terminal] 59 | ---- 60 | nim c helloworld.nim 61 | ---- 62 | 63 | After a successful compilation, we can run our program. 64 | On Linux we can run our program by typing `./helloworld` in the terminal, and on Windows we do it by typing `helloworld.exe`. 65 | 66 | There is also a possibility to both compile and run the program with just one command. 67 | We need to type: 68 | [source, terminal] 69 | ---- 70 | nim c -r helloworld.nim 71 | ---- 72 | 73 | NOTE: `c` is telling Nim to compile the file, and `-r` is telling it to run it immediately. + 74 | To see all compiler options, type `nim --help` in your terminal. 75 | 76 | If you're using VSCode with the Code Runner extension mentioned before, you'll just have to press `Ctrl+Alt+N` and your file will be compiled and run. 77 | 78 | Whichever way you chose to run your program, after a brief moment in the output window (or in your terminal) you should see: 79 | [source, output] 80 | ---- 81 | Hello World! 82 | ---- 83 | 84 | Congratulations, you have successfully run your first Nim program! 85 | 86 | Now you know how to print some stuff on the screen (using the `echo` command), compile your program (typing `nim c programName.nim` in your terminal), and run it (various possibilities). 87 | 88 | We can now start to explore the basic elements which will help us to write simple Nim programs. 89 | -------------------------------------------------------------------------------- /02-variables.adoc: -------------------------------------------------------------------------------- 1 | = Naming values 2 | 3 | 4 | 5 | It is often helpful to give the values in our programs names to help us keep track of things. 6 | If we ask a user for his/her name, we want to store it for the later usage, without asking for it again and again every time we need to do some computation with it. 7 | 8 | In the example `pi = 3.14`, the name `pi` is connected to the value `3.14`. 9 | From our experience, we can tell that the type of a variable `pi` is a (decimal) number. 10 | 11 | Another example would be `firstName = Alice`, where `firstName` is the name of a variable with the value `Alice`. 12 | We would say that the type of this variable is a word. 13 | 14 | In programming languages this works similarly. 15 | These name assignments have their _name_, the _value_, and a _type_. 16 | 17 | 18 | 19 | 20 | == Variable declaration 21 | 22 | 23 | Nim is a https://en.wikipedia.org/wiki/Type_system#STATIC[statically typed] programming language, meaning that the type of an assignment needs to be declared before using the value. 24 | 25 | In Nim we also distinguish values that can change, or mutate, from those that can't, but more on this later. 26 | We can declare a variable (a mutable assignment) using the `var` keyword, just by stating its name and type (the value can be added later) by using this syntax: 27 | [source] 28 | ---- 29 | var : 30 | ---- 31 | 32 | If we already know its value, we can declare a variable and give it a value immediately: 33 | [source] 34 | ---- 35 | var : = 36 | ---- 37 | NOTE: Angular brackets(`<>`) are used to show something you can change. + 38 | So `` is not literally the word `name` in angular brackets but rather any name. 39 | 40 | 41 | Nim also has https://en.wikipedia.org/wiki/Type_inference[type inference] ability: the compiler can automatically detect the type of a name assignment from its value, without explicitly stating the type. 42 | We'll look more into the various types in the <>. 43 | 44 | So we can assign a variable without an explicit type like this: 45 | [source] 46 | ---- 47 | var = 48 | ---- 49 | 50 | 51 | An example of this in Nim looks like this: 52 | [source] 53 | ---- 54 | var a: int # <1> 55 | var b = 7 # <2> 56 | ---- 57 | <1> Variable `a` is of type `int` (integer) with no value explicitly set. 58 | <2> Variable `b` has a value of `7`. Its type is automatically detected as an integer. 59 | 60 | 61 | When assigning names it is important to choose names that mean something for your program. 62 | Simply naming them `a`, `b`, `c`, and so forth will quickly become confusing. 63 | It is not possible to use spaces in a name, as that would split it into two. 64 | So if the name you choose consists of more than one word the usual way is to write it in `camelCase` style (notice that the first letter in a name should be lowercase). 65 | 66 | Note however that Nim is both case- and underscore-insensitive meaning that `helloWorld` and `hello_world` would be the same name. 67 | The exception to this is the first character, which _is_ case-sensitive. 68 | Names can also include both numbers and other UTF-8 characters, even emojis should you wish that, but keep in mind you and possibly others will have to type them. 69 | 70 | 71 | {nbsp} 72 | 73 | Instead of typing `var` for each variable, multiple variables (not necessarily of the same type) can be declared in the same `var` block. 74 | In Nim, blocks are parts of code with the same indentation (same number of spaces before the first character), and the default indentation level is two spaces. 75 | You will see such blocks everywhere in a Nim program, not only for assigning names. 76 | 77 | [source] 78 | ---- 79 | var 80 | c = -11 81 | d = "Hello" 82 | e = '!' 83 | ---- 84 | 85 | NOTE: In Nim tabs are not allowed as indentation. + 86 | You can set up your code editor to convert pressing `Tab` to any number of spaces. + 87 | In VS Code, the default setting is to convert `Tab` to four spaces. 88 | This is easily overridden in settings (`Ctrl+,`) by setting `"editor.tabSize": 2`. 89 | 90 | As previously mentioned variables are mutable, i.e. their value can change (multiple times), but their type must stay the same as declared. 91 | 92 | [source] 93 | ---- 94 | var f = 7 <1> 95 | 96 | f = -3 <2> 97 | f = 19 98 | f = "Hello" # error <3><4> 99 | ---- 100 | <1> Variable `f` has an initial value of `7` and its type is inferred as `int`. 101 | <2> The value of `f` is first changed to `-3`, and then to `19`. Both of these are integers, the same as the original value. 102 | <3> Trying to change the value of `f` to `"Hello"` produces an error because `Hello` is not a number, and this would change the type of `f` from an integer to a string. 103 | <4> `# error` is a comment. Comments in Nim code are written after a `#` character. Everything after it on the same line will be ignored. 104 | 105 | 106 | 107 | 108 | == Immutable assignment 109 | 110 | Unlike variables declared with `var` keyword, two more types of assignment exist in Nim, whose value cannot change, one declared with the `const` keyword, and the other declared with the `let` keyword. 111 | 112 | 113 | 114 | === Const 115 | 116 | The value of an immutable assignment declared with `const` keyword must be known at compile time (before the program is run). 117 | 118 | For example, we can declare the acceleration of gravity as `const g = 9.81` or pi as `const pi = 3.14`, as we know their values in advance and these values will not change during the execution of our program. 119 | 120 | [source] 121 | ---- 122 | const g = 35 123 | g = -27 # error <1> 124 | 125 | var h = -5 126 | const i = h + 7 # error <2> 127 | ---- 128 | <1> The value of a constant cannot be changed. 129 | <2> Variable `h` is not evaluated at compile time (it is a variable and its value can change during the execution of a program), consequently the value of constant `i` can't be known at compile time, and this will raise an error. 130 | 131 | In some programming languages it is a common practice to have the names of constants written in `ALL_CAPS`. 132 | Constants in Nim are written just like any other variable. 133 | 134 | 135 | 136 | === Let 137 | 138 | Immutable assignments declared with `let` don't need to be known at compile time, their value can be set at any time during the execution of a program, but once it is set, their value cannot change. 139 | 140 | [source] 141 | ---- 142 | let j = 35 143 | j = -27 # error <1> 144 | 145 | var k = -5 146 | let l = k + 7 <2> 147 | ---- 148 | <1> The value of an immutable cannot be changed. 149 | <2> In contrast to the `const` example above, this works. 150 | 151 | In practice, you will see/use `let` more frequently than `const`. 152 | 153 | While you could use `var` for everything, your default choice should be `let`. 154 | Use `var` only for the variables which will be modified. 155 | -------------------------------------------------------------------------------- /03-types.adoc: -------------------------------------------------------------------------------- 1 | = Basic data types 2 | 3 | 4 | 5 | == Integers 6 | 7 | As seen in the previous chapter, integers are numbers which are written without a fractional component and without a decimal point. 8 | 9 | For example: `32`, `-174`, `0`, `10_000_000` are all integers. 10 | Notice that we can use `_` as a thousands separator, to make larger numbers more readable (it is easier to see that we're talking about 10 million when it's written as 10_000_000 rather than as 10000000). 11 | 12 | The usual mathematical operators -- addition (`+`), subtraction (`-`), multiplication (`*`), and division (`/`) -- work as one would expect. 13 | The first three operations always produce integers, while dividing two integers always gives a floating point number (a number with a decimal point) as a result, even if two numbers can be divided without a remainder. 14 | 15 | Integer division (division where the fractional part is discarded) can be achieved with the `div` operator. 16 | An operator `mod` is used if one is interested in the remainder (modulus) of an integer division. 17 | The result of these two operations is always an integer. 18 | 19 | [source] 20 | .integers.nim 21 | ---- 22 | include::{source-dir}/integers.nim[] 23 | ---- 24 | <1> The `echo` command will print to the screen everything that follows it separated by commas. In this case, it first prints the string ``a + b = ``, and then after it, in the same row, it prints the result of the expression `a + b`. 25 | 26 | We can compile and run the above code, and the output should be: 27 | [source, output] 28 | ---- 29 | a + b = 15 30 | a - b = 7 31 | a * b = 44 32 | a / b = 2.75 33 | a div b = 2 34 | a mod b = 3 35 | ---- 36 | 37 | 38 | 39 | 40 | == Floats 41 | 42 | Floating-point numbers, or floats for short, are an https://en.wikipedia.org/wiki/Floating-point_arithmetic[approximate representation] of real numbers. 43 | 44 | For example: `2.73`, `-3.14`, `5.0`, `4e7` are floats. 45 | Notice that we can use scientific notation for large floats, where the number after the `e` is the exponent. 46 | In this example, `4e7` is a notation representing `4 * 10^7`. 47 | 48 | We can also use the four basic mathematical operations between two floats. 49 | Operators `div` and `mod` are not defined for floats. 50 | 51 | [source] 52 | .floats.nim 53 | ---- 54 | include::{source-dir}/floats.nim[] 55 | ---- 56 | 57 | [source, output] 58 | ---- 59 | c + d = 9.0 <1> 60 | c - d = 4.5 61 | c * d = 15.1875 62 | c / d = 3.0 <1> 63 | ---- 64 | <1> Notice that in the addition and division examples, even though we get a number without a decimal part, the result is still of the floating type. 65 | 66 | The precedence of mathematical operations is as one would expect: multiplication and division have higher priority than addition and subtraction. 67 | 68 | [source] 69 | ---- 70 | echo 2 + 3 * 4 71 | echo 24 - 8 / 4 72 | ---- 73 | 74 | [source, output] 75 | ---- 76 | 14 77 | 22.0 78 | ---- 79 | 80 | 81 | 82 | 83 | === Converting floats and integers 84 | 85 | Mathematical operations between variables of different numerical types are not possible in Nim, and they will produce an error: 86 | [source] 87 | ---- 88 | let 89 | e = 5 90 | f = 23.456 91 | 92 | echo e + f # error 93 | ---- 94 | 95 | The values of variables need to be converted to the same type. 96 | Conversion is straight-forward: to convert to an integer, we use the `int` function, and to convert to a float the `float` function is used. 97 | [source] 98 | ---- 99 | let 100 | e = 5 101 | f = 23.987 102 | 103 | echo float(e) <1> 104 | echo int(f) <2> 105 | 106 | echo float(e) + f <3> 107 | echo e + int(f) <4> 108 | ---- 109 | <1> Printing a `float` version of an integer `e`. (`e` remains of integer type) 110 | <2> Printing an `int` version of a float `f`. 111 | <3> Both operands are floats and can be added. 112 | <4> Both operands are integers and can be added. 113 | 114 | 115 | [source, output] 116 | ---- 117 | 5.0 118 | 23 119 | 28.987 120 | 28 121 | ---- 122 | 123 | NOTE: When using the `int` function to convert a float to an integer no rounding will be performed. 124 | The number simply drops any decimals. + 125 | To perform rounding we must call another function, but for that we must know a bit more about how to use Nim. 126 | 127 | 128 | 129 | 130 | == Characters 131 | 132 | The `char` type is used for representing a single https://en.wikipedia.org/wiki/ASCII[ASCII] character. 133 | 134 | Chars are written between two single ticks (`'`). 135 | Chars can be letters, symbols, or single digits. 136 | Multiple digits or multiple letters produce an error. 137 | [source] 138 | ---- 139 | let 140 | h = 'z' 141 | i = '+' 142 | j = '2' 143 | k = '35' # error 144 | l = 'xy' # error 145 | ---- 146 | 147 | 148 | 149 | 150 | == Strings 151 | 152 | Strings can be described as a series of characters. 153 | Their content is written between two double quotes (`"`). 154 | 155 | We might think of strings as words, but they can contain more than one word, some symbols, or digits. 156 | 157 | [source] 158 | .strings.nim 159 | ---- 160 | include::{source-dir}/strings.nim[] 161 | ---- 162 | <1> An empty string. 163 | <2> This is not a number (int). It is inside double quotes, making it a string. 164 | <3> Even though this is only one character, it is not a char because it is enclosed inside of double quotes. 165 | 166 | 167 | 168 | 169 | 170 | === Special characters 171 | 172 | If we try to print the following string: 173 | [source] 174 | ---- 175 | echo "some\nim\tips" 176 | ---- 177 | the result might surprise us: 178 | [source, output] 179 | ---- 180 | some 181 | im ips 182 | ---- 183 | 184 | This is because there are several characters which have a special meaning. 185 | They are used by prepending the escape character `\` to them. 186 | 187 | * `\n` is a newline character 188 | * `\t` is a tab character 189 | * `\\` is a backslash (since one `\` is used as the escape character) 190 | 191 | If we wanted to print the above example as it was written, we have two possibilities: 192 | 193 | * Use `\\` instead of `\` to print backslashes, or 194 | * Use raw strings which have syntax `r"..."` (putting a letter `r` immediately before the first quote), in which there are no escape characters and no special meanings: everything is printed as it is. 195 | 196 | [source] 197 | ---- 198 | echo "some\\nim\\tips" 199 | echo r"some\nim\tips" 200 | ---- 201 | 202 | [source, output] 203 | ---- 204 | some\nim\tips 205 | some\nim\tips 206 | ---- 207 | 208 | There are more special characters than the ones listed above, and they are all found in the https://nim-lang.org/docs/manual.html#lexical-analysis-string-literals[Nim manual]. 209 | 210 | 211 | 212 | 213 | 214 | === String concatenation 215 | 216 | Strings in Nim are mutable, meaning their content can change. 217 | With the `add` function we can add (append) either another string or a char to an existing string. 218 | If we don't want to change the original string, we can also concatenate (join together) strings with the `&` operator, this returns a new string. 219 | 220 | [source] 221 | .stringConcat.nim 222 | ---- 223 | include::{source-dir}/stringConcat.nim[] 224 | ---- 225 | <1> If we plan to modify strings, they should be declared as `var`. 226 | <2> Adding another string modifies the existing string `p` in-place, changing its value. 227 | <3> We can also add a `char` to a string. 228 | <4> Concatenating two strings produces a new string, without modifying the original strings. 229 | 230 | [source, output] 231 | ---- 232 | p is now: abcdef 233 | q is now: xyz 234 | concat: abcdefxyz 235 | p is still: abcdef 236 | q is still: xyz 237 | ---- 238 | 239 | 240 | 241 | 242 | 243 | 244 | == Boolean 245 | 246 | A boolean (or just `bool`) data type can only have two values: `true` or `false`. 247 | Booleans are usually used for control flow (see <>), and they are often a result of relational operators. 248 | 249 | The usual naming convention for boolean variables is to write them as a simple yes/no (true/false) question, e.g. `isEmpty`, `isFinished`, `isMoving`, etc. 250 | 251 | 252 | 253 | === Relational operators 254 | 255 | Relational operators test the relation between two entities, which must be comparable. 256 | 257 | To compare if two values are the same, `==` (two equal signs) is used. 258 | Do not confuse this with `=`, which is used for assignment as we saw earlier. 259 | 260 | Here are all the relational operators defined for integers: 261 | 262 | [source] 263 | .relationalOperators.nim 264 | ---- 265 | include::{source-dir}/relationalOperators.nim[lines=1..11] 266 | ---- 267 | 268 | [source, output] 269 | ---- 270 | g is greater than h: false 271 | g is smaller than h: true 272 | g is equal to h: false 273 | g is not equal to h: true 274 | g is greater or equal to h: false 275 | g is smaller or equal to h: true 276 | ---- 277 | 278 | 279 | We can also compare characters and strings: 280 | 281 | [source] 282 | .relationalOperators.nim 283 | ---- 284 | include::{source-dir}/relationalOperators.nim[lines=13..30] 285 | ---- 286 | <1> All uppercase letters come before lowercase letters. 287 | <2> String comparison works char-by-char. First three characters are the same, and character `b` is smaller than character `z`. 288 | <3> String length doesn't matter for comparison if their characters are not identical. 289 | <4> Shorter string is smaller than the longer one. 290 | 291 | [source, output] 292 | ---- 293 | true 294 | false 295 | true 296 | true 297 | true 298 | ---- 299 | 300 | 301 | 302 | 303 | === Logical operators 304 | 305 | Logical operators are used to test the truthiness of an expression consisting of one or more boolean values. 306 | 307 | * Logical `and` returns `true` only if both members are `true` 308 | * Logical `or` returns `true` if there is at least one member which is `true` 309 | * Logical `xor` returns `true` if one member is true, but the other is not 310 | * Logical `not` negates the truthiness of its member: changing `true` to `false`, and vice versa (it is the only logical operator that takes just one operand) 311 | 312 | [source] 313 | .logicalOperators.nim 314 | ---- 315 | include::{source-dir}/logicalOperators.nim[] 316 | ---- 317 | 318 | [source, output] 319 | ---- 320 | T and T: true 321 | T and F: false 322 | F and F: false 323 | --- 324 | T or T: true 325 | T or F: true 326 | F or F: false 327 | --- 328 | T xor T: false 329 | T xor F: true 330 | F xor F: false 331 | --- 332 | not T: false 333 | not F: true 334 | ---- 335 | 336 | 337 | Relational and logical operators can be combined together to form more complex expressions. 338 | 339 | For example: `(5 < 7) and (11 + 9 == 32 - 2*6)` will become `true and (20 == 20)`, which becomes `true and true`, and in the end this will give the final result of `true`. 340 | 341 | 342 | 343 | 344 | == Recap 345 | 346 | This was the longest chapter in this tutorial and we covered a lot of ground. 347 | Take your time to go through each data type and experiment with what you can do with each of them. 348 | 349 | Types might seem like a restriction at first, but they allow the Nim compiler to both make your code faster, and make sure you're not doing something wrong by accident -- this is especially beneficial in large code bases. 350 | 351 | Now you know the basic data types and several operations on them, which should be enough to do some simple calculations in Nim. 352 | Test your knowledge by doing the following exercises. 353 | 354 | 355 | 356 | 357 | == Exercises 358 | 359 | 1. Create an immutable variable containing your age (in years). Print your age in days. (1 year = 365 days) 360 | 2. Check if your age is divisible by 3. (Hint: use `mod`) 361 | 3. Create an immutable variable containing your height in centimeters. Print your height in inches. (1 in = 2.54 cm) 362 | 4. A pipe has a 3/8 inch diameter. Express the diameter in centimeters. 363 | 5. Create an immutable variable containing your first name, and another one containing your last name. Make a variable `fullName` by concatenating the previous two variables. Don't forget to put a whitespace in-between. Print your full name. 364 | 6. Alice earns $400 every 15 days. Bob earns $3.14 per hour and works 8 hours a day, 7 days a week. After 30 days, has Alice earned more than Bob? (Hint: use relational operators) 365 | -------------------------------------------------------------------------------- /04-control-flow.adoc: -------------------------------------------------------------------------------- 1 | = Control flow 2 | 3 | 4 | 5 | So far in our programs every line of code was executed at some point. 6 | Control flow statements allow us to have parts of code which will be executed only if some boolean condition is satisfied. 7 | 8 | If we think of our program as a road we can think of control flow as various branches, and we pick our path depending on some condition. 9 | For example, we will buy eggs only _if_ their price is less than some value. 10 | Or, _if_ it is raining, we will bring an umbrella, otherwise (_else_) we will bring sunglasses. 11 | 12 | Written in https://en.wikipedia.org/wiki/Pseudocode[pseudocode], these two examples would look like this: 13 | 14 | [source] 15 | ---- 16 | if eggPrice < wantedPrice: 17 | buyEggs 18 | 19 | if isRaining: 20 | bring umbrella 21 | else: 22 | bring sunglasses 23 | ---- 24 | 25 | Nim syntax is very similar, as you'll see below. 26 | 27 | 28 | 29 | 30 | 31 | == If statement 32 | 33 | An if statement as shown above is the simplest way to branch our program. 34 | 35 | The Nim syntax for writing if statement is: 36 | 37 | [source] 38 | ---- 39 | if : <1> 40 | <2> 41 | ---- 42 | 43 | <1> The `condition` must be of boolean type: either a boolean variable or a relational and/or logical expression. 44 | <2> All lines following the `if` line which are indented two spaces make the same block and will be executed only if the condition is `true`. 45 | 46 | If statements can be nested, i.e. inside one if-block there can be another if statement. 47 | 48 | [source] 49 | .if.nim 50 | ---- 51 | include::{source-dir}/if.nim[] 52 | ---- 53 | <1> The first condition is true, the second is false -- inner `echo` is not executed. 54 | <2> Both conditions are true and both lines are printed. 55 | <3> The first condition is false -- all lines inside of its block will be skipped, nothing is printed. 56 | <4> Using the logical `and` inside of the `if` statement. 57 | 58 | [source, output] 59 | ---- 60 | a is smaller than b 61 | b is smaller than c 62 | not only that, b is *much* smaller than c 63 | ---- 64 | 65 | 66 | 67 | === Else 68 | 69 | Else follows after an if-block and allows us to have a branch of code which will be executed when the condition in the if statement is not true. 70 | 71 | [source] 72 | .else.nim 73 | ---- 74 | include::{source-dir}/else.nim[] 75 | ---- 76 | 77 | [source, output] 78 | ---- 79 | d is a large number 80 | e is a small number 81 | ---- 82 | 83 | NOTE: If you only want to execute a block if the statement is `false`, you can simply negate the condition with the `not` operator. 84 | 85 | 86 | 87 | 88 | === Elif 89 | 90 | Elif is short for "else if", and enables us to chain multiple if statements together. 91 | 92 | The program tests every statement until it finds one which is true. 93 | After that, all further statements are ignored. 94 | 95 | [source] 96 | .elif.nim 97 | ---- 98 | include::{source-dir}/elif.nim[] 99 | ---- 100 | 101 | [source, output] 102 | ---- 103 | f is larger than 1000 104 | g is smaller than 1000 105 | ---- 106 | 107 | NOTE: In the case of `g`, even though `g` satisfies all three conditions, only the first branch is executed, automatically skipping all the other branches. 108 | 109 | 110 | 111 | 112 | == Case 113 | 114 | A case statement is another way to only choose one of multiple possible paths, similar to the if statement with multiple ``elif``s. 115 | A `case` statement, however, doesn't take multiple boolean conditions, but rather any value with distinct states and a path for each possible value. 116 | 117 | Code written with in if-elif block looking like this: 118 | 119 | [source] 120 | ---- 121 | if x == 5: 122 | echo "Five!" 123 | elif x == 7: 124 | echo "Seven!" 125 | elif x == 10: 126 | echo "Ten!" 127 | else: 128 | echo "unknown number" 129 | ---- 130 | can be written with case statement like this: 131 | 132 | [source] 133 | ---- 134 | case x 135 | of 5: 136 | echo "Five!" 137 | of 7: 138 | echo "Seven!" 139 | of 10: 140 | echo "Ten!" 141 | else: 142 | echo "unknown number" 143 | ---- 144 | 145 | Unlike the if statement, case statement must cover _all_ possible cases. 146 | If one is not interested in some of those cases, `else: discard` can be used. 147 | 148 | [source] 149 | .case.nim 150 | ---- 151 | include::{source-dir}/case.nim[] 152 | ---- 153 | <1> Even though we are interested in only three values of `h`, we must include this line to cover all other possible cases (all other characters). Without it, the code would not compile. 154 | 155 | [source, output] 156 | ---- 157 | You've chosen y 158 | ---- 159 | 160 | 161 | 162 | We can also use multiple values for each branch if the same action should happen for more than one value. 163 | [source] 164 | .multipleCase.nim 165 | ---- 166 | include::{source-dir}/multipleCase.nim[] 167 | ---- 168 | 169 | [source, output] 170 | ---- 171 | i is odd 172 | ---- 173 | -------------------------------------------------------------------------------- /05-loops.adoc: -------------------------------------------------------------------------------- 1 | = Loops 2 | 3 | 4 | 5 | Loops are another control flow construct which allow us to run some parts of code multiple times. 6 | 7 | In this chapter we will meet two kinds of loops: 8 | 9 | * for-loop: run a known number of times 10 | * while-loop: run as long some condition is satisfied 11 | 12 | 13 | == For loop 14 | 15 | Syntax of a for-loop is: 16 | [source] 17 | ---- 18 | for in : 19 | 20 | ---- 21 | 22 | Traditionally, `i` is often used as a `loopVariable` name, but any other name can be used. 23 | That variable will be available only inside the loop. 24 | Once the loop has finished, the value of the variable is discarded. 25 | 26 | The `iterable` is any object we can iterate through. 27 | Of the types already mentioned, strings are iterable objects. 28 | (More iterable types will be introduced in the <>.) 29 | 30 | All lines in the `loop body` are executed at every loop, which allows us to efficiently write repeating parts of code. 31 | 32 | {nbsp} 33 | 34 | If we want to iterate through a range of (integer) numbers in Nim, the syntax for the `iterable` is `start .. finish` where `start` and `finish` are numbers. 35 | This will iterate through all the numbers between `start` and `finish`, including both `start` and `finish`. 36 | For the default range iterable, `start` needs to be smaller than `finish`. 37 | 38 | If we want to iterate _until_ a number (not including it), we can use `..<`: 39 | 40 | [source] 41 | .for1.nim 42 | ---- 43 | include::{source-dir}/for1.nim[] 44 | ---- 45 | <1> Iterating through a range of numbers using `..` -- both ends are included in the range. 46 | <2> Iterating through the same range using `..<` -- it iterates until the higher end, not including it. 47 | 48 | [source, output] 49 | ---- 50 | 5 51 | 6 52 | 7 53 | 8 54 | 9 55 | 56 | 5 57 | 6 58 | 7 59 | 8 60 | ---- 61 | 62 | 63 | 64 | If we want to iterate through a range of numbers with a step size different than one, `countup` is used. With `countup` we define the starting value, the stopping value (included in the range), and the step size. 65 | 66 | [source] 67 | .for2.nim 68 | ---- 69 | include::{source-dir}/for2.nim[lines=1..2] 70 | ---- 71 | <1> Counting up from zero to 16, with a step size of 4. The end (16) is included in the range. 72 | 73 | [source, output] 74 | ---- 75 | 0 76 | 4 77 | 8 78 | 12 79 | 16 80 | ---- 81 | 82 | 83 | To iterate through a range of numbers where the `start` is larger than `finish`, a similar function called `countdown` is used. 84 | Even if we're counting down, the step size must be positive. 85 | 86 | [source] 87 | .for2.nim 88 | ---- 89 | include::{source-dir}/for2.nim[lines=5..12] 90 | ---- 91 | <1> To iterate from a higher to a lower number, we must use `countdown` (The `..` operator can only be used when the starting value is smaller than the end value). 92 | <2> Even when counting down, the step size must be a positive number. 93 | 94 | [source, output] 95 | ---- 96 | 4 97 | 3 98 | 2 99 | 1 100 | 0 101 | 102 | -3 103 | -5 104 | -7 105 | -9 106 | ---- 107 | 108 | 109 | Since string is an iterable, we can use a for-loop to iterate through each character of the string (this kind of iteration is sometimes called a _for-each_ loop). 110 | 111 | [source] 112 | .for3.nim 113 | ---- 114 | include::{source-dir}/for3.nim[lines=1..4] 115 | ---- 116 | 117 | [source, output] 118 | ---- 119 | a 120 | l 121 | p 122 | h 123 | a 124 | b 125 | e 126 | t 127 | ---- 128 | 129 | 130 | If we also need to have an iteration counter (starting from zero), we can achieve that by using `for , in :` syntax. 131 | This is very practical if you want to iterate through one iterable, and simultaneously access another iterable at the same offset. 132 | 133 | [source] 134 | .for3.nim 135 | ---- 136 | include::{source-dir}/for3.nim[lines=7..9] 137 | ---- 138 | 139 | [source, output] 140 | ---- 141 | letter 0 is: a 142 | letter 1 is: l 143 | letter 2 is: p 144 | letter 3 is: h 145 | letter 4 is: a 146 | letter 5 is: b 147 | letter 6 is: e 148 | letter 7 is: t 149 | ---- 150 | 151 | 152 | 153 | 154 | == While loop 155 | 156 | While loops are similar to if statements, but they keep executing their block of code as long as the condition remains true. 157 | They are used when we don't know in advance how many times the loop will run. 158 | 159 | We must make sure the loop will terminate at some point and not become an https://en.wikipedia.org/wiki/Infinite_loop[infinite loop]. 160 | 161 | [source] 162 | .while.nim 163 | ---- 164 | include::{source-dir}/while.nim[] 165 | ---- 166 | <1> This condition will be checked every time before entering the new loop and executing the code inside of it. 167 | <2> `inc` is used to increment `a` by one. It is the same as writing `a = a + 1` or `a += 1`. 168 | 169 | [source, output] 170 | ---- 171 | a is: 1 172 | a is: 2 173 | a is: 3 174 | final value of a: 4 175 | ---- 176 | 177 | 178 | 179 | 180 | == Break and continue 181 | 182 | The `break` statement is used to prematurely exit from a loop, usually if some condition is met. 183 | 184 | In the next example, if there were no if statement with `break` in it, the loop would continue to run and print until `i` becomes 1000. 185 | With the `break` statement, when `i` becomes 3, we immediately exit the loop (before printing the value of `i`). 186 | 187 | [source] 188 | .break.nim 189 | ---- 190 | include::{source-dir}/break.nim[] 191 | ---- 192 | 193 | [source, output] 194 | ---- 195 | 1 196 | 2 197 | ---- 198 | 199 | {nbsp} 200 | 201 | The `continue` statement starts the next iteration of a loop immediately, without executing the remaining lines of the current iteration. 202 | Notice how 3 and 6 are missing from the output of the following code: 203 | 204 | [source] 205 | .continue.nim 206 | ---- 207 | include::{source-dir}/continue.nim[] 208 | ---- 209 | 210 | [source, output] 211 | ---- 212 | 1 213 | 2 214 | 4 215 | 5 216 | 7 217 | 8 218 | ---- 219 | 220 | 221 | 222 | 223 | == Exercises 224 | 225 | 1. https://en.wikipedia.org/wiki/Collatz_conjecture[Collatz conjecture] is a popular mathematical problem with simple rules. First pick a number. If it is odd, multiply it by three and add one; if it is even, divide it by two. Repeat this procedure until you arrive at one. E.g. 5 -> odd -> 3*5 + 1 = 16 -> even -> 16 / 2 = 8 -> even -> 4 -> 2 -> 1 -> end! + 226 | Pick an integer (as a mutable variable) and create a loop which will print every step of the Collatz conjecture. (Hint: use `div` for division) 227 | 228 | 2. Create an immutable variable containing your full name. Write a for-loop which will iterate through that string and print only the vowels (a, e, i, o, u). (Hint: use `case` statement with multiple values per branch) 229 | 230 | 3. https://en.wikipedia.org/wiki/Fizz_buzz[Fizz buzz] is a kids game sometimes used to test basic programming knowledge. We count numbers from one upwards. If a number is divisible by 3 replace it with _fizz_, if it is divisible by 5 replace it with _buzz_, and if a number is divisible by 15 (both 3 and 5) replace it with _fizzbuzz_. First few rounds would look like this: 1, 2, fizz, 4, buzz, fizz, 7, ... + 231 | Create a program which will print first 30 rounds of Fizz buzz. (Hint: beware of the order of divisibility tests) 232 | 233 | 4. In the previous exercises you have converted inches to centimeters, and vice versa. Create a conversion table with multiple values. For example, the table might look like this: + 234 | ---- 235 | in | cm 236 | ---------------- 237 | 1 | 2.54 238 | 4 | 10.16 239 | 7 | 17.78 240 | 10 | 25.4 241 | 13 | 33.02 242 | 16 | 40.64 243 | 19 | 48.26 244 | ---- 245 | -------------------------------------------------------------------------------- /06-containers.adoc: -------------------------------------------------------------------------------- 1 | = Containers 2 | 3 | 4 | 5 | Containers are data types which contain a collection of items and allow us to access those elements. 6 | Typically a container is also iterable, meaning that we can use them the same way we used strings in the <>. 7 | 8 | For example, a grocery list is a container of items we want to buy, and a list of primes is a container of numbers. 9 | Written in pseudocode: 10 | [source] 11 | ---- 12 | groceryList = [ham, eggs, bread, apples] 13 | primes = [1, 2, 3, 5, 7] 14 | ---- 15 | 16 | 17 | 18 | == Arrays 19 | 20 | An array is the simplest container type. 21 | Arrays are homogeneous, i.e. all elements in an array must have the same type. 22 | Arrays are also of a constant size, meaning that the amount of elements (or rather: the amount of _possible_ elements), must be known at compile-time. 23 | This means that we call arrays a "homogeneous container of a constant length". 24 | 25 | The array type is declared using `array[, ]`, where `length` is the total capacity of the array (number of elements it can fit), and `type` is a type of all its elements. 26 | The declaration can be omitted if both length and type can be inferred from the passed elements. 27 | 28 | The elements of an array are enclosed inside of square brackets. 29 | 30 | [source] 31 | ---- 32 | var 33 | a: array[3, int] = [5, 7, 9] 34 | b = [5, 7, 9] <1> 35 | c = [] # error <2> 36 | d: array[7, string] <3> 37 | ---- 38 | <1> If we provide the values, the length and type of array `b` are known at compile time. Although correct, there is no need to specifically declare it like array `a`. 39 | <2> Neither the length nor the type of the elements can be inferred from this kind of declaration -- this produces an error. 40 | <3> The correct way to declare an empty array (which will be filled later) is to give its length and type, without providing the values of its elements -- array `d` can contain seven strings. 41 | 42 | 43 | Since the length of an array has to be known at compile-time, this will not work: 44 | 45 | [source] 46 | ---- 47 | const m = 3 48 | let n = 5 49 | 50 | var a: array[m, char] 51 | var b: array[n, char] # error <1> 52 | ---- 53 | <1> This produces an error because `n` is declared using `let` -- its value is not known at compile time. We can only use values declared with `const` as a `length` parameter for an array initialization. 54 | 55 | 56 | 57 | 58 | 59 | == Sequences 60 | 61 | Sequences are containers similar to arrays, but their length doesn't have to be known at compile time, and it can change during runtime: we declare only the type of the contained elements with `seq[]`. 62 | Sequences are also homogeneous, i.e. every element in a sequence has to be the same type. 63 | 64 | The elements of a sequence are enclosed between `@[` and `]`. 65 | 66 | [source] 67 | ---- 68 | var 69 | e1: seq[int] = @[] <1> 70 | f = @["abc", "def"] <2> 71 | ---- 72 | <1> The type of an empty sequence must be declared. 73 | <2> The type of a non-empty sequence can be inferred. In this case, it is a sequence containing strings. 74 | 75 | Another way to initialize an empty sequence is to call the `newSeq` procedure. We'll look more at procedure calls in the <> but for now just know that this is also a possibility: 76 | 77 | [source] 78 | ---- 79 | var 80 | e = newSeq[int]() <1> 81 | ---- 82 | <1> Providing the type parameter inside of square brackets allows the procedure to know that it shall return a sequence of a certain type. + 83 | A frequent error is omission of the final `()`, which must be included. 84 | 85 | 86 | We can add new elements to a sequence with the `add` function, similar to how we did with strings. 87 | For this to work the sequence must be mutable (defined with `var`), and the element we're adding must be of the same type as the elements in the sequence. 88 | 89 | [source] 90 | .seq.nim 91 | ---- 92 | include::{source-dir}/seq.nim[] 93 | ---- 94 | <1> Adding a new element of the same type (char). 95 | <2> Adding another sequence containing the same type. 96 | 97 | [source, output] 98 | ---- 99 | @['x', 'y', 'z'] 100 | @['1', '2', '3', 'x', 'y', 'z'] 101 | ---- 102 | 103 | Trying to pass different types to the existing sequences will produce an error: 104 | 105 | [source] 106 | ---- 107 | var i = @[9, 8, 7] 108 | 109 | i.add(9.81) # error <1> 110 | g.add(i) # error <2> 111 | ---- 112 | <1> Trying to add a `float` to a sequence of `int`. 113 | <2> Trying to add a sequence of `int` to a sequence of `char`. 114 | 115 | 116 | Since sequences can vary in length we need a way to get their length, for this we can use the `len` function. 117 | 118 | [source] 119 | ---- 120 | var i = @[9, 8, 7] 121 | echo i.len 122 | 123 | i.add(6) 124 | echo i.len 125 | ---- 126 | 127 | [source, output] 128 | ---- 129 | 3 130 | 4 131 | ---- 132 | 133 | 134 | 135 | 136 | 137 | == Indexing and slicing 138 | 139 | Indexing allows us to get a specific element from a container by its index. 140 | Think of the index as a position inside of the container. 141 | 142 | Nim, like many other programming languages, has zero-based indexing, meaning that the first element in a container has the index zero, the second element has the index one, etc. 143 | 144 | If we want to index "from the back", it is done by using the `^` prefix. 145 | The last element (first from the back) has index `^1`. 146 | 147 | The syntax for indexing is `[]`. 148 | 149 | [source] 150 | .indexing.nim 151 | ---- 152 | include::{source-dir}/indexing.nim[lines=1..4] 153 | ---- 154 | <1> Zero-based indexing: the element at index 1 is `b`. 155 | <2> Getting the last element. 156 | 157 | [source, output] 158 | ---- 159 | b 160 | e 161 | ---- 162 | 163 | {nbsp} 164 | 165 | Slicing allows us to get a series of elements with one call. 166 | It uses the same syntax as ranges (introduced in the <>). 167 | 168 | If we use `start .. stop` syntax, both ends are included in the slice. 169 | Using `start ..< stop` syntax, the `stop` index is not included in the slice. 170 | 171 | The syntax for slicing is `[ .. ]`. 172 | 173 | [source] 174 | .indexing.nim 175 | ---- 176 | include::{source-dir}/indexing.nim[lines=6..8] 177 | ---- 178 | 179 | [source, output] 180 | ---- 181 | @[a, b, c, d] 182 | @[a, b, c] 183 | ---- 184 | 185 | 186 | Both indexing and slicing can be used to assign new values to the existing mutable containers and strings. 187 | 188 | [source] 189 | .assign.nim 190 | ---- 191 | include::{source-dir}/assign.nim[] 192 | ---- 193 | <1> Array of length 5 has indexes from zero to four. We will assign a value to each element of the array. 194 | <2> Assigning (changing) the second element (index 1) of a sequence. 195 | <3> Changing characters of a string at indexes 8 and 9. 196 | 197 | 198 | [source, output] 199 | ---- 200 | [0, 7, 14, 21, 28] 201 | @['p', 'q', 'r'] 202 | Tom and Barry 203 | ---- 204 | 205 | 206 | 207 | 208 | == Tuples 209 | 210 | Both of the containers we've seen so far have been homogeneous. 211 | Tuples, on the other hand, contain heterogeneous data, i.e. elements of a tuple can be of different types. 212 | Similarly to arrays, tuples have fixed-size. 213 | 214 | The elements of a tuple are enclosed inside of parentheses. 215 | 216 | [source] 217 | .tuples.nim 218 | ---- 219 | include::{source-dir}/tuples.nim[lines=1..3] 220 | ---- 221 | <1> Tuples can contain fields of different types. In this case: `string`, `int`, and `char`. 222 | 223 | [source, output] 224 | ---- 225 | (Field0: "Banana", Field1: 2, Field2: 'c') 226 | ---- 227 | 228 | 229 | 230 | We can also name each field in a tuple to distinguish them. 231 | This can be used for accessing the elements of the tuple, instead of indexing. 232 | 233 | [source] 234 | .tuples.nim 235 | ---- 236 | include::{source-dir}/tuples.nim[lines=5..10] 237 | ---- 238 | <1> Changing the value of a field by using the field's index. 239 | <2> Changing the value of a field by using the field's name. 240 | 241 | [source, output] 242 | ---- 243 | (name: "Apple", weight: 7, rating: 'c') 244 | ---- 245 | 246 | 247 | 248 | 249 | 250 | == Exercises 251 | 252 | 1. Create an empty array which can contain ten integers. 253 | * Fill that array with numbers 10, 20, ..., 100. (Hint: use loops) 254 | * Print only the elements of that array that are on odd indices (values 20, 40, ...). 255 | * Multiply elements on even indices by 5. Print the modified array. 256 | 257 | 2. Re-do the <<_exercises_2, Collatz conjecture exercise>>, but this time instead of printing each step, add it to a sequence. 258 | * Pick a starting number. Interesting choices, among others, are 9, 19, 25 and 27. 259 | * Create a sequence whose only member is that starting number 260 | * Using the same logic as before, keep adding elements to the sequence until you reach 1 261 | * Print the length of the sequence, and the sequence itself 262 | 263 | 3. Find the number in a range from 2 to 100 which will produce the longest Collatz sequence. 264 | * For each number in the given range calculate its Collatz sequence 265 | * If the length of current sequence is longer than the previous record, save the current length and the starting number as a new record (you can use the tuple `(longestLength, startingNumber)` or two separate variables) 266 | * Print the starting number which gives the longest sequence, and its length 267 | -------------------------------------------------------------------------------- /07-procedures.adoc: -------------------------------------------------------------------------------- 1 | = Procedures 2 | 3 | 4 | 5 | Procedures, or _functions_ as they are called in some other programming languages, are parts of code that perform a specific task, packaged as a unit. 6 | The benefit of grouping code together like this is that we can _call_ these procedures instead of writing all the code over again when we wish to use the procedure's code. 7 | 8 | In some of the previous chapters we've looked at the Collatz conjecture in various different scenarios. 9 | By wrapping up the Collatz conjecture logic into a procedure we could have called the same code for all the exercises. 10 | 11 | So far we have used many built-in procedures, such as `echo` for printing, `add` for adding elements to a sequence, `inc` to increase the value of an integer, `len` to get the length of a container, etc. 12 | Now we'll see how to create and use our own procedures. 13 | 14 | Some of the advantages of using procedures are: 15 | 16 | * Reducing code duplication 17 | * Easier to read code as we can name pieces by what they do 18 | * Decomposing a complex task into simpler steps 19 | 20 | As mentioned in the beginning of this section, procedures are often called functions in other languages. 21 | This is actually a bit of a misnomer if we consider the mathematical definition of a function. 22 | Mathematical functions take a set of arguments (like `f(x, y)`, where `f` is a function, and `x` and `y` are its arguments) and _always_ return the same answer for the same input. 23 | 24 | Programmatic procedures on the other hand don't always return the same output for a given input. 25 | Sometimes they don't return anything at all. 26 | This is because our computer programs can store state in the variables we mentioned earlier which procedures can read and change. 27 | In Nim, the word `func` is currently reserved to be used as the more mathematically correct kind of function, forcing no side-effects. 28 | 29 | 30 | 31 | 32 | == Declaring a procedure 33 | 34 | Before we can use (call) our procedure, we need to create it and define what it does. 35 | 36 | A procedure is declared by using the `proc` keyword and the procedure name, followed by the input parameters and their type inside of parentheses, and the last part is a colon and the type of the value returned from a procedure, like this: 37 | 38 | [source] 39 | ---- 40 | proc (: , : , ...): 41 | ---- 42 | 43 | The body of a procedure is written in the indented block following the declaration appended with a `=` sign. 44 | 45 | [source] 46 | .callProcs.nim 47 | ---- 48 | include::{source-dir}/callProcs.nim[lines=1..8] 49 | ---- 50 | <1> Declaring procedure called `findMax`, which has two parameters, `x` and `y`, and it returns an `int` type. 51 | <2> To return a value from a procedure, we use the `return` keyword. 52 | 53 | 54 | {nbsp} 55 | 56 | [source] 57 | ---- 58 | proc echoLanguageRating(language: string) = <1> 59 | case language 60 | of "Nim", "nim", "NIM": 61 | echo language, " is the best language!" 62 | else: 63 | echo language, " might be a second-best language." 64 | ---- 65 | <1> The `echoLanguageRating` procedure just echoes the given name, it doesn't return anything, so the return type is not declared. 66 | 67 | 68 | {nbsp} 69 | 70 | Normally we're not allowed to change any of the parameters we are given. 71 | Doing something like this will throw an error: 72 | 73 | [source] 74 | ---- 75 | proc changeArgument(argument: int) = 76 | argument += 5 77 | 78 | var ourVariable = 10 79 | changeArgument(ourVariable) 80 | ---- 81 | 82 | In order for this to work we need to allow Nim, and the programmer using our procedure, to change the argument by declaring it as a variable: 83 | 84 | [source] 85 | ---- 86 | proc changeArgument(argument: var int) = <1> 87 | argument += 5 88 | 89 | var ourVariable = 10 90 | changeArgument(ourVariable) 91 | echo ourVariable 92 | changeArgument(ourVariable) 93 | echo ourVariable 94 | ---- 95 | <1> Notice how `argument` is now declared as a `var int` and not just as an `int`. 96 | 97 | [source, output] 98 | ---- 99 | 15 100 | 20 101 | ---- 102 | 103 | This of course means that the name we pass it must be declared as a variable as well, passing in something assigned with `const` or `let` will throw an error. 104 | 105 | While it is good practice to pass things as arguments it is also possible to use names declared outside the procedure, both variables and constants: 106 | 107 | [source] 108 | ---- 109 | var x = 100 110 | 111 | proc echoX() = 112 | echo x <1> 113 | x += 1 <2> 114 | 115 | echoX() 116 | echoX() 117 | ---- 118 | <1> Here we access the outside variable `x`. 119 | <2> We can also update its value, since it's declared as a variable. 120 | [source, output] 121 | ---- 122 | 100 123 | 101 124 | ---- 125 | 126 | 127 | == Calling the procedures 128 | 129 | After we have declared a procedure, we can call it. 130 | The usual way of calling procedures/functions in many programming languages is to state its name and provide the arguments in the parentheses, like this: 131 | 132 | [source] 133 | ---- 134 | (, , ...) 135 | ---- 136 | 137 | The result from calling a procedure can be stored in a variable. 138 | 139 | If we want to call our `findMax` procedure from the above example, and save the return value in a variable we can do that with: 140 | 141 | [source] 142 | .callProcs.nim 143 | ---- 144 | include::{source-dir}/callProcs.nim[lines=10..18] 145 | ---- 146 | <1> The result from the function `findMax` is here named `c`, and is called with the results of our first two calls (`findMax(987, 321)`). 147 | 148 | [source, output] 149 | ---- 150 | 987 151 | 321 152 | 987 153 | ---- 154 | 155 | {nbsp} 156 | 157 | Nim, unlike many other languages, also supports https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax[Uniform Function Call Syntax], which allows many different ways of calling procedures. 158 | 159 | This one is a call where the first argument is written before the function name, and the rest of the parameters are stated in parentheses: 160 | 161 | [source] 162 | ---- 163 | .(, ...) 164 | ---- 165 | 166 | We have used this syntax when we were adding elements to an existing sequence (`.add()`), as this makes it more readable and expresses our intent more clearly than writing `add(, )`. 167 | We can also omit the parentheses around the arguments: 168 | 169 | [source] 170 | ---- 171 | , , ... 172 | ---- 173 | 174 | We've seen this style being used when we call the `echo` procedure, and when calling the `len` procedure without any arguments. 175 | These two can also be combined like this, but this syntax however is not seen very often: 176 | 177 | [source] 178 | ---- 179 | . , , ... 180 | ---- 181 | 182 | 183 | {nbsp} 184 | 185 | The uniform call syntax allows for more readable chaining of multiple procedures: 186 | 187 | [source] 188 | .ufcs.nim 189 | ---- 190 | include::{source-dir}/ufcs.nim[] 191 | ---- 192 | <1> If multiple parameters are of the same type, we can declare their type in this compact way. 193 | <2> First we add `a` and `b`, then the result of that operation (2 + 3 = 5) is passed as the first parameter to the `multi` procedure, where it is multiplied by `c` (5 * 4 = 20). 194 | <3> First we multiply `c` and `b`, then the result of that operation (4 * 3 = 12) is passed as the first parameter to the `plus` procedure, where it is added with `a` (12 + 2 = 14). 195 | 196 | [source, output] 197 | ---- 198 | true 199 | true 200 | 20 201 | 14 202 | ---- 203 | 204 | 205 | 206 | 207 | == Result variable 208 | 209 | In Nim, every procedure that returns a value has an implicitly declared and initialized (with a default value) `result` variable. 210 | The procedure will return the value of this `result` variable when it reaches the end of its indented block, even with no `return` statement. 211 | 212 | [source] 213 | .result.nim 214 | ---- 215 | include::{source-dir}/result.nim[lines=1..9] 216 | ---- 217 | <1> The return type is `int`. The `result` variable is initialized with the default value for `int`: `0`. 218 | <2> When the end of the procedure is reached, the value of `result` is returned. 219 | 220 | [source, output] 221 | ---- 222 | 33 223 | ---- 224 | 225 | Note that this procedure is here to demonstrate the `result` variable, and it is not 100% correct: 226 | if you would pass a sequence containing only negative numbers, this procedure would return `0` (which is _not_ contained in the sequence). 227 | 228 | 229 | 230 | {nbsp} 231 | 232 | 233 | WARNING: Beware! 234 | In older Nim versions (before Nim 0.19.0), the default value of strings and sequences was https://en.wikipedia.org/wiki/Null_pointer[`nil`], and when we would use them as returning types, the `result` variable would need to be initialized as an empty string (`""`) or as an empty sequence (`@[]`). 235 | 236 | [source] 237 | .result.nim 238 | ---- 239 | include::{source-dir}/result.nim[lines=11..20] 240 | ---- 241 | <1> In Nim version 0.19.0 and newer, this line is not needed -- sequences are automatically initialized as empty sequences. + 242 | In older Nim versions, sequences must be initialized, and without this line the compiler would throw an error. (Notice that `var` must _not_ be used, as `result` is already implicitly declared.) 243 | 244 | [source, output] 245 | ---- 246 | @[1, 43, 57] 247 | ---- 248 | 249 | 250 | {nbsp} 251 | 252 | Inside of a procedure we can also call other procedures. 253 | 254 | [source] 255 | .filterOdds.nim 256 | ---- 257 | include::{source-dir}/filterOdds.nim[] 258 | ---- 259 | <1> Once again, this line is not needed in the newer versions of Nim. 260 | <2> Calling the previously declared procedure. Its return type is `bool` and can be used in the if-statement. 261 | <3> The third way of calling a procedure, as we saw above. 262 | 263 | [source, output] 264 | ---- 265 | @[6, 9, 0, 3] 266 | @[3] 267 | @[45390, 3219] 268 | ---- 269 | 270 | 271 | 272 | 273 | 274 | == Forward declaration 275 | 276 | As mentioned in the very beginning of this section we can declare a procedure without a code block. 277 | The reason for this is that we have to declare procedures before we can call them, doing this will not work: 278 | 279 | [source] 280 | ---- 281 | echo 5.plus(10) # error <1> 282 | 283 | proc plus(x, y: int): int = <2> 284 | return x + y 285 | ---- 286 | <1> This will throw an error as `plus` isn't defined yet. 287 | <2> Here we define `plus`, but since it's after we use it Nim doesn't know about it yet. 288 | 289 | The way to get around this is what's called a forward declaration: 290 | 291 | [source] 292 | ---- 293 | proc plus(x, y: int): int <1> 294 | 295 | echo 5.plus(10) <2> 296 | 297 | proc plus(x, y: int): int = <3> 298 | return x + y 299 | ---- 300 | <1> Here we tell Nim that it should consider the `plus` procedure to exist with this definition. 301 | <2> Now we are free to use it in our code, this will work. 302 | <3> This is were `plus` is actually implemented, this must of course match our previous definition. 303 | 304 | 305 | 306 | 307 | 308 | == Exercises 309 | 310 | 1. Create a procedure which will greet a person (print "Hello ") based on the provided name. Create a sequence of names. Greet each person using the created procedure. 311 | 312 | 2. Create a procedure `findMax3` which will return the largest of three values. 313 | 314 | 3. Points in 2D plane can be represented as `tuple[x, y: float]`. Write a procedure which will receive two points and return a new point which is a sum of those two points (add x's and y's separately). 315 | 316 | 4. Create two procedures `tick` and `tock` which echo out the words "tick" and "tock". Have a global variable to keep track of how many times they have run, and run one from the other until the counter reaches 20. The expected output is to get lines with "tick" and "tock" alternating 20 times. (Hint: use forward declarations.) 317 | 318 | NOTE: You can press Ctrl+C to stop execution of a program if you enter an infinite loop. 319 | 320 | Test all procedures by calling them with different parameters. 321 | -------------------------------------------------------------------------------- /08-modules.adoc: -------------------------------------------------------------------------------- 1 | = Modules 2 | 3 | 4 | 5 | So far we have used the functionality which is available by default every time we start a new Nim file. 6 | This can be extended with modules, which give more functionality for some specific topic. 7 | 8 | Some of the most used Nim modules are: 9 | 10 | * `strutils`: additional functionality when dealing with strings 11 | * `sequtils`: additional functionality for sequences 12 | * `math`: mathematical functions (logarithms, square roots, ...), trigonometry (sin, cos, ...) 13 | * `times`: measure and deal with time 14 | 15 | But there are many more, both in what's called the https://nim-lang.org/docs/lib.html[standard library] and in the https://nimble.directory/[nimble package manager]. 16 | 17 | 18 | 19 | 20 | == Importing a module 21 | 22 | If we want to import a module and all of its functionality, all we have to do is put `import ` in our file. 23 | This is commonly done on the top of the file so we can easily see what our code uses. 24 | 25 | [source] 26 | .stringutils.nim 27 | ---- 28 | include::{source-dir}/stringutils.nim[] 29 | ---- 30 | <1> Importing https://nim-lang.org/docs/strutils.html[strutils]. 31 | <2> Using `split` from `strutils` module. It splits the string in a sequence of words. 32 | <3> `toUpperAscii` converts all ASCII letters to uppercase. 33 | <4> `repeat` is also from `strutils` module, and it repeats either a character or a whole string the requested amount of times. 34 | 35 | [source, output] 36 | ---- 37 | @["My", "string", "with", "whitespace."] 38 | MY STRING WITH WHITESPACE. 39 | !!!!! 40 | ---- 41 | 42 | NOTE: To the users coming from other programming languages (especially Python), the way that imports work in Nim might seem "wrong". If that's the case, https://narimiran.github.io/2019/07/01/nim-import.html[this] is the recommended reading. 43 | 44 | {nbsp} 45 | 46 | 47 | [source] 48 | .maths.nim 49 | ---- 50 | include::{source-dir}/maths.nim[] 51 | ---- 52 | <1> Importing https://nim-lang.org/docs/math.html[math]. 53 | <2> Converting degrees to radians with `degToRad`. 54 | <3> `sin` takes radians. We round (also from `math` module) the result to at most 2 decimal places. (Otherwise the result would be: 0.4999999999999999) 55 | <4> Math module also has `^` operator for calculating powers of a number. 56 | 57 | [source, output] 58 | ---- 59 | 0.5235987755982988 60 | 0.5 61 | 32 62 | ---- 63 | 64 | 65 | 66 | 67 | == Creating our own 68 | 69 | Often times we have so much code in a project that it makes sense to split it into pieces that each does a certain thing. 70 | If you create two files side by side in a folder, let's call them `firstFile.nim` and `secondFile.nim`, you can import one from the other as a module: 71 | 72 | [source] 73 | .firstFile.nim 74 | ---- 75 | proc plus*(a, b: int): int = <1> 76 | return a + b 77 | 78 | proc minus(a, b: int): int = <2> 79 | return a - b 80 | ---- 81 | <1> Notice how the `plus` procedure now has an asterisk (`*`) after its name, this tells Nim that another file importing this one will be able to use this procedure. 82 | <2> By contrast this will not be visible when importing this file. 83 | 84 | [source] 85 | .secondFile.nim 86 | ---- 87 | import firstFile <1> 88 | 89 | echo plus(5, 10) <2> 90 | echo minus(10, 5) # error <3> 91 | ---- 92 | <1> Here we import `firstFile.nim`. We don't need to put the `.nim` extension on here. 93 | <2> This will work fine and output `15` as it's declared in `firstFile` and visible to us. 94 | <3> However this will throw an error as the `minus` procedure is not visible since it doesn't have an asterisk behind it's name. 95 | 96 | 97 | In case you have more than these two files, you might want to organize them in a subdirectory (or more than one subdirectory). 98 | With the following directory structure: 99 | 100 | [source, output] 101 | ---- 102 | . 103 | ├── myOtherSubdir 104 | │   ├── fifthFile.nim 105 | │   └── fourthFile.nim 106 | ├── mySubdir 107 | │   └── thirdFile.nim 108 | ├── firstFile.nim 109 | └── secondFile.nim 110 | ---- 111 | 112 | if you wanted to import all other files in your `secondFile.nim` this is how you would do it: 113 | 114 | [source] 115 | .secondFile.nim 116 | ---- 117 | import firstFile 118 | import mySubdir/thirdFile 119 | import myOtherSubdir / [fourthFile, fifthFile] 120 | ---- 121 | -------------------------------------------------------------------------------- /09-user-input.adoc: -------------------------------------------------------------------------------- 1 | = Interacting with user input 2 | 3 | 4 | 5 | Using the stuff we've introduced so far (basic data types and containers, control flow, loops) allows us to make quite a few simple programs. 6 | 7 | In this chapter we will learn how to make our programs more interactive. 8 | For that we need an option to read data from a file, or ask a user for an input. 9 | 10 | 11 | 12 | 13 | == Reading from a file 14 | 15 | Let's say we have a text file called `people.txt` in the same directory as our Nim code. 16 | The contents of that file looks like this: 17 | 18 | [source] 19 | .people.txt 20 | ---- 21 | include::{source-dir}/people.txt[] 22 | 23 | ---- 24 | 25 | We want to use the contents of that file in our program, as a list (sequence) of names. 26 | 27 | [source] 28 | .readFromFile.nim 29 | ---- 30 | include::{source-dir}/readFromFile.nim[] 31 | ---- 32 | <1> To read contents of a file, we use the `readFile` procedure, and we provide a path to the file from which to read (if the file is in the same directory as our Nim program, providing a filename is enough). The result is a multiline string. 33 | <2> To split a multiline string into a sequence of strings (each string contains all the contents of a single line) we use `splitLines` from the `strutils` module. 34 | 35 | [source, output] 36 | ---- 37 | Alice A. 38 | Bob B. 39 | Carol C. 40 | <1> 41 | @["Alice A.", "Bob B.", "Carol C.", ""] <2> 42 | ---- 43 | <1> There was a final new line (empty last line) in the original file, which is also present here. 44 | <2> Because of the final new line, our sequence is longer than we expected/wanted. 45 | 46 | To solve the problem of a final new line, we can use the `strip` procedure from `strutils` after we have read from a file. 47 | All this does is remove any so-called whitespace from the start and end of our string. 48 | Whitespace is simply any character that makes some space, new-lines, spaces, tabs, etc. 49 | 50 | [source] 51 | .readFromFile2.nim 52 | ---- 53 | include::{source-dir}/readFromFile2.nim[] 54 | ---- 55 | <1> Using `strip` provides the expected results. 56 | 57 | [source, output] 58 | ---- 59 | Alice A. 60 | Bob B. 61 | Carol C. 62 | @["Alice A.", "Bob B.", "Carol C."] 63 | ---- 64 | 65 | 66 | 67 | 68 | == Reading user input 69 | 70 | If we want to interact with a user, we must be able to ask them for an input, and then process it and use it. 71 | We need to read from https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)[standard input (stdin)] by passing `stdin` to the `readLine` procedure. 72 | 73 | [source] 74 | .interaction1.nim 75 | ---- 76 | include::{source-dir}/interaction1.nim[] 77 | ---- 78 | <1> The type of `name` is inferred to be a string. 79 | 80 | 81 | [source, output] 82 | ---- 83 | Please enter your name: 84 | <1> 85 | ---- 86 | <1> Waiting for user input. After we write our name and press `Enter`, the program will continue. 87 | 88 | [source, output] 89 | ---- 90 | Please enter your name: 91 | Alice 92 | Hello Alice, nice to meet you! 93 | ---- 94 | 95 | NOTE: If you are using an outdated version of VS Code, you cannot run this the usual way (using `Ctrl+Alt+N`) because output window doesn't allow user inputs -- you need to run these examples in the terminal. + 96 | With the newer versions of VS Code there is no such limitation. 97 | 98 | 99 | 100 | 101 | == Dealing with numbers 102 | 103 | Reading from a file or from a user input always gives a string as a result. 104 | If we would like to use numbers, we need to convert strings to numbers: we again use the `strutils` module and use `parseInt` to convert to integers or `parseFloat` to convert into a float. 105 | 106 | [source] 107 | .interaction2.nim 108 | ---- 109 | include::{source-dir}/interaction2.nim[] 110 | ---- 111 | <1> Convert a string to an integer. When written like this, we trust our user to give a valid integer. What would happen if a user inputs `'79` or `ninety-three`? Try it yourself. 112 | 113 | [source, output] 114 | ---- 115 | Please enter your year of birth: 116 | 1934 117 | You are 84 years old. 118 | ---- 119 | 120 | {nbsp} 121 | 122 | If we have file `numbers.txt` in the same directory as our Nim code, with the following content: 123 | 124 | [source] 125 | .numbers.txt 126 | ---- 127 | include::{source-dir}/numbers.txt[] 128 | ---- 129 | 130 | and we want to read that file and find the sum and average of the numbers provided, we can do something like this: 131 | 132 | [source] 133 | .interaction3.nim 134 | ---- 135 | include::{source-dir}/interaction3.nim[] 136 | ---- 137 | <1> We import multiple modules. `strutils` gives us `strip` and `splitLines`, `sequtils` gives `map`, and `math` gives `sum`. 138 | <2> We strip the final new line, and split lines to create a sequence of strings. 139 | <3> `map` works by applying a procedure (in this case `parseFloat`) to each member of a container. In other words, we convert each string to a float, returning a new sequence of floats. 140 | <4> Using `sum` from `math` module to give us the sum of all elements in a sequence. 141 | <5> We need to convert the length of a sequence to float, because `sumNums` is a float. 142 | 143 | [source, output] 144 | ---- 145 | 226.15 146 | 45.23 147 | ---- 148 | 149 | 150 | 151 | 152 | == Exercises 153 | 154 | 1. Ask a user for their height and weight. Calculate their https://en.wikipedia.org/wiki/Body_mass_index[BMI]. Report them the BMI value and the category. 155 | 156 | 2. Repeat <<_exercises_4, Collatz conjecture exercise>> so your program asks a user for a starting number. Print the resulting sequence. 157 | 158 | 3. Ask a user for a string they want to have reversed. Create a procedure which takes a string and returns a reversed version. For example, if user types `Nim-lang`, the procedure should return `gnal-miN`. (Hint: use indexing and `countdown`) 159 | -------------------------------------------------------------------------------- /10-conclusion.adoc: -------------------------------------------------------------------------------- 1 | = Conclusion 2 | 3 | 4 | 5 | It is time to conclude this tutorial. 6 | Hopefully this has been useful to you, and you managed to make your first steps in programming and/or the Nim programming language. 7 | 8 | These have only been the basics and we've only scratched the surface, but this should be enough to enable you to make simple programs and solve some simple tasks or puzzles. 9 | Nim has a lot more to offer, and hopefully you will continue to explore its possibilities. 10 | 11 | 12 | 13 | 14 | 15 | == Next steps 16 | 17 | If you want to continue learning from Nim tutorials: 18 | 19 | * https://nim-lang.org/docs/tut1.html[Official Nim tutorial] 20 | 21 | * https://nim-by-example.github.io/[Nim by example] 22 | 23 | 24 | If you want to solve some programming puzzles: 25 | 26 | * http://adventofcode.com/[Advent of Code]: Series of interesting puzzles released every December. Archive of old puzzles (from 2015 onwards) is available. 27 | 28 | * https://projecteuler.net/[Project Euler]: Mostly mathematical tasks. 29 | 30 | 31 | 32 | {nbsp} 33 | 34 | Happy coding! 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 narimiran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | sources=$(wildcard *.adoc) $(wildcard code/*.nim) 2 | 3 | # create web version 4 | index.html: $(sources) styles/style.css 5 | asciidoctor -o $@ -a nofooter 00-nim-basics.adoc 6 | 7 | # create pdf version 8 | nim-basics.pdf: $(sources) styles/pdf-theme.yml 9 | asciidoctor-pdf -o $@ -a rouge-style=github 00-nim-basics.adoc 10 | 11 | # create epub version 12 | nim-basics.epub: $(sources) styles/epub.css 13 | asciidoctor-epub3 -o $@ -a rouge-style=github 00-nim-basics.adoc 14 | 15 | 16 | .PHONY: web 17 | web: index.html 18 | 19 | .PHONY: pdf 20 | pdf: nim-basics.pdf 21 | 22 | .PHONY: epub 23 | epub: nim-basics.epub 24 | 25 | .PHONY: all 26 | all: web epub #pdf 27 | 28 | .DEFAULT_GOAL := all 29 | 30 | 31 | .PHONY: clean 32 | clean: 33 | rm nim-basics.pdf nim-basics.epub index.html 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nim basics 2 | 3 | These are the source files for [Nim basics](https://narimiran.github.io/nim-basics/) tutorial. 4 | 5 | 6 | 7 | ## Contributing 8 | 9 | Please report any errors to the [issue tracker](https://github.com/narimiran/nim-basics/issues). 10 | 11 | Pull requests are welcome, but before making (significant) changes, please open an issue so it can be discussed what is the best way to tackle the specific problem. 12 | 13 | After making the changes, make sure to build new web and epub versions: 14 | ```bash 15 | make 16 | ``` 17 | 18 | If you just want to see the changes, while working on a significant refactoring, you can just build web version, as it is much faster: 19 | ```bash 20 | make web 21 | ``` 22 | 23 | 24 | ## License 25 | 26 | [MIT License](LICENSE.txt) 27 | -------------------------------------------------------------------------------- /code/assign.nim: -------------------------------------------------------------------------------- 1 | var 2 | k: array[5, int] 3 | l = @['p', 'w', 'r'] 4 | m = "Tom and Jerry" 5 | 6 | for i in 0 .. 4: # <1> 7 | k[i] = 7 * i 8 | echo k 9 | 10 | l[1] = 'q' # <2> 11 | echo l 12 | 13 | m[8 .. 9] = "Ba" # <3> 14 | echo m 15 | -------------------------------------------------------------------------------- /code/break.nim: -------------------------------------------------------------------------------- 1 | var i = 1 2 | 3 | while i < 1000: 4 | if i == 3: 5 | break 6 | echo i 7 | inc i 8 | -------------------------------------------------------------------------------- /code/callProcs.nim: -------------------------------------------------------------------------------- 1 | proc findMax(x: int, y: int): int = # <1> 2 | if x > y: 3 | return x # <2> 4 | else: 5 | return y 6 | # this is inside of the procedure 7 | # this is outside of the procedure 8 | 9 | 10 | let 11 | a = findMax(987, 789) 12 | b = findMax(123, 321) 13 | c = findMax(a, b) # <1> 14 | 15 | echo a 16 | echo b 17 | echo c 18 | -------------------------------------------------------------------------------- /code/case.nim: -------------------------------------------------------------------------------- 1 | let h = 'y' 2 | 3 | case h 4 | of 'x': 5 | echo "You've chosen x" 6 | of 'y': 7 | echo "You've chosen y" 8 | of 'z': 9 | echo "You've chosen z" 10 | else: discard # <1> 11 | -------------------------------------------------------------------------------- /code/chars.nim: -------------------------------------------------------------------------------- 1 | echo 'a' < 'b' 2 | echo '5' == 'f' 3 | echo '!' < '@' 4 | -------------------------------------------------------------------------------- /code/continue.nim: -------------------------------------------------------------------------------- 1 | for i in 1 .. 8: 2 | if (i == 3) or (i == 6): 3 | continue 4 | echo i 5 | -------------------------------------------------------------------------------- /code/conversion.nim: -------------------------------------------------------------------------------- 1 | let 2 | e = 5 3 | f = 23.456 4 | 5 | echo float(e) 6 | echo int(f) 7 | 8 | echo float(e) + f 9 | -------------------------------------------------------------------------------- /code/elif.nim: -------------------------------------------------------------------------------- 1 | let 2 | f = 3456 3 | g = 7 4 | 5 | if f < 10: 6 | echo "f is smaller than 10" 7 | elif f < 100: 8 | echo "f is between 10 and 100" 9 | elif f < 1000: 10 | echo "f is between 100 and 1000" 11 | else: 12 | echo "f is larger than 1000" 13 | 14 | if g < 1000: 15 | echo "g is smaller than 1000" 16 | elif g < 100: 17 | echo "g is smaller than 100" 18 | elif g < 10: 19 | echo "g is smaller than 10" 20 | 21 | -------------------------------------------------------------------------------- /code/else.nim: -------------------------------------------------------------------------------- 1 | let 2 | d = 63 3 | e = 2.718 4 | 5 | if d < 10: 6 | echo "d is a small number" 7 | else: 8 | echo "d is a large number" 9 | 10 | if e < 10: 11 | echo "e is a small number" 12 | else: 13 | echo "e is a large number" 14 | -------------------------------------------------------------------------------- /code/filterOdds.nim: -------------------------------------------------------------------------------- 1 | proc isDivisibleBy3(x: int): bool = 2 | return x mod 3 == 0 3 | 4 | proc filterMultiplesOf3(a: seq[int]): seq[int] = 5 | # result = @[] # <1> 6 | for i in a: 7 | if i.isDivisibleBy3(): # <2> 8 | result.add(i) 9 | 10 | 11 | let 12 | g = @[2, 6, 5, 7, 9, 0, 5, 3] 13 | h = @[5, 4, 3, 2, 1] 14 | i = @[626, 45390, 3219, 4210, 4126] 15 | 16 | echo filterMultiplesOf3(g) 17 | echo h.filterMultiplesOf3() 18 | echo filterMultiplesOf3 i # <3> 19 | -------------------------------------------------------------------------------- /code/floats.nim: -------------------------------------------------------------------------------- 1 | let 2 | c = 6.75 3 | d = 2.25 4 | 5 | echo "c + d = ", c + d 6 | echo "c - d = ", c - d 7 | echo "c * d = ", c * d 8 | echo "c / d = ", c / d 9 | -------------------------------------------------------------------------------- /code/for1.nim: -------------------------------------------------------------------------------- 1 | for n in 5 .. 9: # <1> 2 | echo n 3 | 4 | echo "" 5 | 6 | for n in 5 ..< 9: # <2> 7 | echo n -------------------------------------------------------------------------------- /code/for2.nim: -------------------------------------------------------------------------------- 1 | for n in countup(0, 16, 4): # <1> 2 | echo n 3 | 4 | 5 | for n in countdown(4, 0): # <1> 6 | echo n 7 | 8 | echo "" 9 | 10 | for n in countdown(-3, -9, 2): # <2> 11 | echo n 12 | -------------------------------------------------------------------------------- /code/for3.nim: -------------------------------------------------------------------------------- 1 | let word = "alphabet" 2 | 3 | for letter in word: 4 | echo letter 5 | 6 | 7 | for i, letter in word: 8 | echo "letter ", i, " is: ", letter 9 | -------------------------------------------------------------------------------- /code/helloworld.nim: -------------------------------------------------------------------------------- 1 | echo "Hello World!" 2 | -------------------------------------------------------------------------------- /code/if.nim: -------------------------------------------------------------------------------- 1 | let 2 | a = 11 3 | b = 22 4 | c = 999 5 | 6 | if a < b: 7 | echo "a is smaller than b" 8 | if 10*a < b: # <1> 9 | echo "not only that, a is *much* smaller than b" 10 | 11 | if b < c: 12 | echo "b is smaller than c" 13 | if 10*b < c: # <2> 14 | echo "not only that, b is *much* smaller than c" 15 | 16 | if a+b > c: # <3> 17 | echo "a and b are larger than c" 18 | if 1 < 100 and 321 > 123: # <4> 19 | echo "did you know that 1 is smaller than 100?" 20 | echo "and 321 is larger than 123! wow!" 21 | -------------------------------------------------------------------------------- /code/indexing.nim: -------------------------------------------------------------------------------- 1 | let j = ['a', 'b', 'c', 'd', 'e'] 2 | 3 | echo j[1] # <1> 4 | echo j[^1] # <2> 5 | 6 | echo j[0 .. 3] 7 | echo j[0 ..< 3] 8 | -------------------------------------------------------------------------------- /code/integers.nim: -------------------------------------------------------------------------------- 1 | let 2 | a = 11 3 | b = 4 4 | 5 | echo "a + b = ", a + b # <1> 6 | echo "a - b = ", a - b 7 | echo "a * b = ", a * b 8 | echo "a / b = ", a / b 9 | echo "a div b = ", a div b 10 | echo "a mod b = ", a mod b 11 | -------------------------------------------------------------------------------- /code/interaction1.nim: -------------------------------------------------------------------------------- 1 | echo "Please enter your name:" 2 | let name = readLine(stdin) # <1> 3 | 4 | echo "Hello ", name, ", nice to meet you!" 5 | -------------------------------------------------------------------------------- /code/interaction2.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | 3 | echo "Please enter your year of birth:" 4 | let yearOfBirth = readLine(stdin).parseInt() # <1> 5 | 6 | let age = 2018 - yearOfBirth 7 | 8 | echo "You are ", age, " years old." 9 | -------------------------------------------------------------------------------- /code/interaction3.nim: -------------------------------------------------------------------------------- 1 | import strutils, sequtils, math # <1> 2 | 3 | let 4 | strNums = readFile("numbers.txt").strip().splitLines() # <2> 5 | nums = strNums.map(parseFloat) # <3> 6 | 7 | let 8 | sumNums = sum(nums) # <4> 9 | average = sumNums / float(nums.len) # <5> 10 | 11 | echo sumNums 12 | echo average 13 | -------------------------------------------------------------------------------- /code/logicalOperators.nim: -------------------------------------------------------------------------------- 1 | echo "T and T: ", true and true 2 | echo "T and F: ", true and false 3 | echo "F and F: ", false and false 4 | echo "---" 5 | echo "T or T: ", true or true 6 | echo "T or F: ", true or false 7 | echo "F or F: ", false or false 8 | echo "---" 9 | echo "T xor T: ", true xor true 10 | echo "T xor F: ", true xor false 11 | echo "F xor F: ", false xor false 12 | echo "---" 13 | echo "not T: ", not true 14 | echo "not F: ", not false 15 | -------------------------------------------------------------------------------- /code/maths.nim: -------------------------------------------------------------------------------- 1 | import math # <1> 2 | 3 | let 4 | c = 30.0 # degrees 5 | cRadians = c.degToRad() # <2> 6 | 7 | echo cRadians 8 | echo sin(cRadians).round(2) # <3> 9 | 10 | echo 2^5 # <4> 11 | -------------------------------------------------------------------------------- /code/multipleCase.nim: -------------------------------------------------------------------------------- 1 | let i = 7 2 | 3 | case i 4 | of 0: 5 | echo "i is zero" 6 | of 1, 3, 5, 7, 9: 7 | echo "i is odd" 8 | of 2, 4, 6, 8: 9 | echo "i is even" 10 | else: 11 | echo "i is too large" 12 | -------------------------------------------------------------------------------- /code/numbers.txt: -------------------------------------------------------------------------------- 1 | 27.3 2 | 98.24 3 | 11.93 4 | 33.67 5 | 55.01 6 | -------------------------------------------------------------------------------- /code/people.txt: -------------------------------------------------------------------------------- 1 | Alice A. 2 | Bob B. 3 | Carol C. 4 | -------------------------------------------------------------------------------- /code/readFromFile.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | 3 | let contents = readFile("people.txt") # <1> 4 | echo contents 5 | 6 | let people = contents.splitLines() # <2> 7 | echo people 8 | -------------------------------------------------------------------------------- /code/readFromFile2.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | 3 | let contents = readFile("people.txt").strip() # <1> 4 | echo contents 5 | 6 | let people = contents.splitLines() 7 | echo people 8 | -------------------------------------------------------------------------------- /code/relationalOperators.nim: -------------------------------------------------------------------------------- 1 | let 2 | g = 31 3 | h = 99 4 | 5 | echo "g is greater than h: ", g > h 6 | echo "g is smaller than h: ", g < h 7 | echo "g is equal to h: ", g == h 8 | echo "g is not equal to h: ", g != h 9 | echo "g is greater or equal to h: ", g >= h 10 | echo "g is smaller or equal to h: ", g <= h 11 | 12 | 13 | let 14 | i = 'a' 15 | j = 'd' 16 | k = 'Z' 17 | 18 | echo i < j 19 | echo i < k # <1> 20 | 21 | let 22 | m = "axyb" 23 | n = "axyz" 24 | o = "ba" 25 | p = "ba " 26 | 27 | echo m < n # <2> 28 | echo n < o # <3> 29 | echo o < p # <4> 30 | -------------------------------------------------------------------------------- /code/result.nim: -------------------------------------------------------------------------------- 1 | proc findBiggest(a: seq[int]): int = # <1> 2 | for number in a: 3 | if number > result: 4 | result = number 5 | # the end of proc # <2> 6 | 7 | let d = @[3, -5, 11, 33, 7, -15] 8 | echo findBiggest(d) 9 | 10 | 11 | proc keepOdds(a: seq[int]): seq[int] = 12 | # result = @[] # <1> 13 | for number in a: 14 | if number mod 2 == 1: 15 | result.add(number) 16 | 17 | 18 | let f = @[1, 6, 4, 43, 57, 34, 98] 19 | echo keepOdds(f) 20 | -------------------------------------------------------------------------------- /code/seq.nim: -------------------------------------------------------------------------------- 1 | var 2 | g = @['x', 'y'] 3 | h = @['1', '2', '3'] 4 | 5 | g.add('z') # <1> 6 | echo g 7 | 8 | h.add(g) # <2> 9 | echo h 10 | -------------------------------------------------------------------------------- /code/stringConcat.nim: -------------------------------------------------------------------------------- 1 | var # <1> 2 | p = "abc" 3 | q = "xy" 4 | r = 'z' 5 | 6 | p.add("def") # <2> 7 | echo "p is now: ", p 8 | 9 | q.add(r) # <3> 10 | echo "q is now: ", q 11 | 12 | echo "concat: ", p & q # <4> 13 | 14 | echo "p is still: ", p 15 | echo "q is still: ", q 16 | -------------------------------------------------------------------------------- /code/strings.nim: -------------------------------------------------------------------------------- 1 | let 2 | m = "word" 3 | n = "A sentence with interpunction." 4 | o = "" # <1> 5 | p = "32" # <2> 6 | q = "!" # <3> -------------------------------------------------------------------------------- /code/stringutils.nim: -------------------------------------------------------------------------------- 1 | import strutils # <1> 2 | 3 | let 4 | a = "My string with whitespace." 5 | b = '!' 6 | 7 | echo a.split() # <2> 8 | echo a.toUpperAscii() # <3> 9 | echo b.repeat(5) # <4> 10 | 11 | 12 | -------------------------------------------------------------------------------- /code/tuples.nim: -------------------------------------------------------------------------------- 1 | let n = ("Banana", 2, 'c') # <1> 2 | echo n 3 | 4 | 5 | var o = (name: "Banana", weight: 2, rating: 'c') 6 | 7 | o[1] = 7 # <1> 8 | o.name = "Apple" # <2> 9 | echo o 10 | -------------------------------------------------------------------------------- /code/ufcs.nim: -------------------------------------------------------------------------------- 1 | proc plus(x, y: int): int = # <1> 2 | return x + y 3 | 4 | proc multi(x, y: int): int = 5 | return x * y 6 | 7 | let 8 | a = 2 9 | b = 3 10 | c = 4 11 | 12 | echo a.plus(b) == plus(a, b) 13 | echo c.multi(a) == multi(c, a) 14 | 15 | 16 | echo a.plus(b).multi(c) # <2> 17 | echo c.multi(b).plus(a) # <3> 18 | -------------------------------------------------------------------------------- /code/while.nim: -------------------------------------------------------------------------------- 1 | var a = 1 2 | 3 | while a*a < 10: # <1> 4 | echo "a is: ", a 5 | inc a # <2> 6 | 7 | echo "final value of a: ", a 8 | -------------------------------------------------------------------------------- /nim-basics.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narimiran/nim-basics/887683b3f2e61836653ad49cb6a466752ca06f8c/nim-basics.epub -------------------------------------------------------------------------------- /nim-basics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narimiran/nim-basics/887683b3f2e61836653ad49cb6a466752ca06f8c/nim-basics.pdf -------------------------------------------------------------------------------- /rouge-github.css: -------------------------------------------------------------------------------- 1 | pre.rouge table td { padding: 5px; } 2 | pre.rouge table pre { margin: 0; } 3 | pre.rouge, pre.rouge .w { 4 | color: #24292f; 5 | background-color: #f6f8fa; 6 | } 7 | pre.rouge .k, pre.rouge .kd, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kt, pre.rouge .kv { 8 | color: #cf222e; 9 | } 10 | pre.rouge .gr { 11 | color: #f6f8fa; 12 | } 13 | pre.rouge .gd { 14 | color: #82071e; 15 | background-color: #ffebe9; 16 | } 17 | pre.rouge .nb { 18 | color: #953800; 19 | } 20 | pre.rouge .nc { 21 | color: #953800; 22 | } 23 | pre.rouge .no { 24 | color: #953800; 25 | } 26 | pre.rouge .nn { 27 | color: #953800; 28 | } 29 | pre.rouge .sr { 30 | color: #116329; 31 | } 32 | pre.rouge .na { 33 | color: #116329; 34 | } 35 | pre.rouge .nt { 36 | color: #116329; 37 | } 38 | pre.rouge .gi { 39 | color: #116329; 40 | background-color: #dafbe1; 41 | } 42 | pre.rouge .ges { 43 | font-weight: bold; 44 | font-style: italic; 45 | } 46 | pre.rouge .kc { 47 | color: #0550ae; 48 | } 49 | pre.rouge .l, pre.rouge .ld, pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx { 50 | color: #0550ae; 51 | } 52 | pre.rouge .sb { 53 | color: #0550ae; 54 | } 55 | pre.rouge .bp { 56 | color: #0550ae; 57 | } 58 | pre.rouge .ne { 59 | color: #0550ae; 60 | } 61 | pre.rouge .nl { 62 | color: #0550ae; 63 | } 64 | pre.rouge .py { 65 | color: #0550ae; 66 | } 67 | pre.rouge .nv, pre.rouge .vc, pre.rouge .vg, pre.rouge .vi, pre.rouge .vm { 68 | color: #0550ae; 69 | } 70 | pre.rouge .o, pre.rouge .ow { 71 | color: #0550ae; 72 | } 73 | pre.rouge .gh { 74 | color: #0550ae; 75 | font-weight: bold; 76 | } 77 | pre.rouge .gu { 78 | color: #0550ae; 79 | font-weight: bold; 80 | } 81 | pre.rouge .s, pre.rouge .sa, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .se, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1, pre.rouge .ss { 82 | color: #0a3069; 83 | } 84 | pre.rouge .nd { 85 | color: #8250df; 86 | } 87 | pre.rouge .nf, pre.rouge .fm { 88 | color: #8250df; 89 | } 90 | pre.rouge .err { 91 | color: #f6f8fa; 92 | background-color: #82071e; 93 | } 94 | pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cp, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs { 95 | color: #6e7781; 96 | } 97 | pre.rouge .gl { 98 | color: #6e7781; 99 | } 100 | pre.rouge .gt { 101 | color: #6e7781; 102 | } 103 | pre.rouge .ni { 104 | color: #24292f; 105 | } 106 | pre.rouge .si { 107 | color: #24292f; 108 | } 109 | pre.rouge .ge { 110 | color: #24292f; 111 | font-style: italic; 112 | } 113 | pre.rouge .gs { 114 | color: #24292f; 115 | font-weight: bold; 116 | } -------------------------------------------------------------------------------- /rouge-gruvbox.css: -------------------------------------------------------------------------------- 1 | pre.rouge table td { padding: 5px; } 2 | pre.rouge table pre { margin: 0; } 3 | pre.rouge, pre.rouge .w { 4 | color: #fbf1c7; 5 | background-color: #282828; 6 | } 7 | pre.rouge .err { 8 | color: #fb4934; 9 | background-color: #282828; 10 | font-weight: bold; 11 | } 12 | pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs { 13 | color: #928374; 14 | font-style: italic; 15 | } 16 | pre.rouge .cp { 17 | color: #8ec07c; 18 | } 19 | pre.rouge .nt { 20 | color: #fb4934; 21 | } 22 | pre.rouge .o, pre.rouge .ow { 23 | color: #fbf1c7; 24 | } 25 | pre.rouge .p, pre.rouge .pi { 26 | color: #fbf1c7; 27 | } 28 | pre.rouge .gi { 29 | color: #b8bb26; 30 | background-color: #282828; 31 | } 32 | pre.rouge .gd { 33 | color: #fb4934; 34 | background-color: #282828; 35 | } 36 | pre.rouge .gh { 37 | color: #b8bb26; 38 | font-weight: bold; 39 | } 40 | pre.rouge .ge { 41 | font-style: italic; 42 | } 43 | pre.rouge .ges { 44 | font-weight: bold; 45 | font-style: italic; 46 | } 47 | pre.rouge .gs { 48 | font-weight: bold; 49 | } 50 | pre.rouge .k, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kv { 51 | color: #fb4934; 52 | } 53 | pre.rouge .kc { 54 | color: #d3869b; 55 | } 56 | pre.rouge .kt { 57 | color: #fabd2f; 58 | } 59 | pre.rouge .kd { 60 | color: #fe8019; 61 | } 62 | pre.rouge .s, pre.rouge .sb, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1 { 63 | color: #b8bb26; 64 | font-style: italic; 65 | } 66 | pre.rouge .si { 67 | color: #b8bb26; 68 | font-style: italic; 69 | } 70 | pre.rouge .sr { 71 | color: #b8bb26; 72 | font-style: italic; 73 | } 74 | pre.rouge .sa { 75 | color: #fb4934; 76 | } 77 | pre.rouge .se { 78 | color: #fe8019; 79 | } 80 | pre.rouge .nn { 81 | color: #8ec07c; 82 | } 83 | pre.rouge .nc { 84 | color: #8ec07c; 85 | } 86 | pre.rouge .no { 87 | color: #d3869b; 88 | } 89 | pre.rouge .na { 90 | color: #b8bb26; 91 | } 92 | pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx { 93 | color: #d3869b; 94 | } 95 | pre.rouge .ss { 96 | color: #83a598; 97 | } -------------------------------------------------------------------------------- /styles/epub-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narimiran/nim-basics/887683b3f2e61836653ad49cb6a466752ca06f8c/styles/epub-cover.png -------------------------------------------------------------------------------- /styles/epub.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: separate; 3 | border-spacing: 1.2em 0.6em; 4 | font-size: 0.95em; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: border-box; 9 | -moz-box-sizing: border-box; 10 | -webkit-box-sizing: border-box; 11 | } 12 | 13 | #header, #content, #footnotes, #footer { 14 | width: 100%; 15 | margin-left: 0em; 16 | margin-right: 0em; 17 | } 18 | 19 | body { 20 | background: #fcfaf7; 21 | color: #010d11; 22 | text-align: justify; 23 | margin-top: -1.8em; 24 | /* margin-left: 0em; */ 25 | font-family: "Fira Sans", "Open Sans", "Noto Sans", "DejaVu Sans", sans-serif; 26 | line-height: 1.5; 27 | tab-size: 4; 28 | -moz-osx-font-smoothing: grayscale; 29 | -webkit-font-smoothing: antialiased; 30 | } 31 | 32 | h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6 { 33 | font-weight: 600; 34 | font-style: normal; 35 | text-align: left; 36 | color: #cb4b16; 37 | margin-top: 2.5em; 38 | } 39 | 40 | h1 { 41 | margin-top: 4.0em; 42 | font-size: 1.75em; 43 | } 44 | 45 | h2 { 46 | font-size: 1.33em; 47 | } 48 | 49 | h3 { 50 | font-size: 1.125em; 51 | font-style: italic; 52 | } 53 | 54 | code { 55 | font-family: "Fira Mono", "Hack", "Noto Sans Mono", "DejaVu Sans Mono", monospace; 56 | font-size: 0.9em; 57 | font-weight: 400; 58 | } 59 | 60 | a { 61 | color: #268bd2; 62 | text-decoration-color: #7daed1; 63 | } 64 | 65 | a:hover, a:focus { 66 | color: #cb4b16; 67 | text-decoration-color: #cb4b16; 68 | } 69 | 70 | p { 71 | font-size: 1em; 72 | text-rendering: optimizeLegibility; 73 | } 74 | 75 | 76 | *:not(pre)>code { 77 | padding: 0.0em 0.35ex; 78 | background-color: #e5e7e8; 79 | border-radius: 6px; 80 | -webkit-border-radius: 6px; 81 | word-wrap: break-word; 82 | } 83 | 84 | pre.rouge { 85 | padding: 1.0em; 86 | background-color: #e5e7e8; 87 | } 88 | 89 | pre, pre>code { 90 | line-height: 1.30; 91 | font-family: "Fira Mono", "Hack", "Noto Sans Mono", "DejaVu Sans Mono", monospace; 92 | font-size: 0.9em; 93 | } 94 | 95 | .admonitionblock { 96 | margin-left: 1em; 97 | margin-right: 1em; 98 | font-style: italic; 99 | } 100 | 101 | .literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { 102 | background: #eeebe2; 103 | padding: 10px; 104 | } 105 | 106 | .listingblock>.title { 107 | line-height: 1.0; 108 | font-size: 0.8em; 109 | color: #cb4b16; 110 | font-weight: 400; 111 | font-style: italic; 112 | margin-bottom: -0.5em; 113 | } 114 | 115 | .conum[data-value] { 116 | display: inline-block; 117 | color: #fcfaf7; 118 | background-color: #7b868a; 119 | border-radius: 5px; 120 | -webkit-border-radius: 5px; 121 | text-align: center; 122 | font-size: 0.8em; 123 | width: 3.0em; 124 | line-height: 1.5em; 125 | margin-right: 1em; 126 | font-style: normal; 127 | font-weight: bold; 128 | } 129 | 130 | hr { 131 | border: solid #7b868a; 132 | border-width: 1px 0 0; 133 | margin-top: 5em; 134 | } 135 | 136 | em, i { 137 | font-style: italic; 138 | line-height: inherit; 139 | } 140 | 141 | strong, b { 142 | font-weight: bold; 143 | line-height: inherit; 144 | } 145 | 146 | .conum[data-value]+b { 147 | display: none; 148 | } 149 | 150 | .conum[data-value]:after { 151 | content: attr(data-value) 152 | } 153 | 154 | pre .conum[data-value] { 155 | position: relative; 156 | top: -.125em; 157 | } 158 | 159 | b.conum * { 160 | color: inherit!important; 161 | } 162 | 163 | li p { 164 | margin-top: -0.5em; 165 | padding-left: 0.5em; 166 | } 167 | 168 | #toc.toc2 #toctitle { 169 | margin-top: 0; 170 | margin-bottom: .8rem; 171 | font-size: 1.25em; 172 | } 173 | #toc.toc2>ul { 174 | font-size: 0.875em; 175 | padding-left: 0.5em; 176 | } 177 | 178 | #toc.toc2 ul ul { 179 | padding-left: 0.5em; 180 | } 181 | 182 | #toc.toc2 a { 183 | text-decoration: none; 184 | } 185 | 186 | #toc.toc2 li { 187 | margin-left: 0.5em; 188 | margin-top: 0em; 189 | margin-bottom: 0em; 190 | text-align: left; 191 | } 192 | -------------------------------------------------------------------------------- /styles/pdf-theme.yml: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | page: 4 | layout: portrait 5 | margin: [0.85in, 1.25in, 1.0in, 1.25in] 6 | marin-inner: 2.5in 7 | margin-outer: 0.5in 8 | size: A4 9 | numbering-start-at: title 10 | 11 | base: 12 | font-size: 11 13 | font-family: Noto Serif 14 | line-height: 1.25 15 | 16 | heading: 17 | h1-font-size: 20 18 | h2-font-size: 16 19 | h3-font-size: 12 20 | font-style: bold 21 | margin-top: 14 22 | margin-bottom: 12 23 | part-break-before: always 24 | chapter-break-before: auto 25 | min-height-after: 1.5in 26 | 27 | admonition: 28 | font-size: 10 29 | 30 | running-content: 31 | start-at: body 32 | 33 | literal: 34 | background-color: #f0f0f0 35 | font-color: #3f3f3f 36 | font-family: M+ 1mn 37 | border-offset: 1 38 | 39 | footer: 40 | height: 0.7in 41 | verso: 42 | left: 43 | content: '*{page-number}* | {chapter-title}' 44 | recto: 45 | right: 46 | content: '{chapter-title} | *{page-number}*' 47 | -------------------------------------------------------------------------------- /styles/style.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: separate; 3 | border-spacing: 0.2em 0.6em; 4 | font-size: 1.06em; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: border-box; 9 | -moz-box-sizing: border-box; 10 | -webkit-box-sizing: border-box; 11 | } 12 | 13 | #header, #content, #footnotes, #footer { 14 | width: 100%; 15 | margin-left: auto; 16 | margin-right: auto; 17 | max-width: 45em; 18 | position: relative; 19 | padding-left: 1em; 20 | padding-right: 1em 21 | } 22 | 23 | body { 24 | background: #fcfaf7; 25 | color: #010d11; 26 | text-align: justify; 27 | margin-top: -1.8em; 28 | font-family: "Fira Sans", "Open Sans", "Noto Sans", "DejaVu Sans", sans-serif; 29 | line-height: 1.5; 30 | tab-size: 4; 31 | -moz-osx-font-smoothing: grayscale; 32 | -webkit-font-smoothing: antialiased; 33 | } 34 | 35 | h1, h2, h3, #toctitle, .sidebarblock>.content>.title, h4, h5, h6 { 36 | font-weight: 600; 37 | font-style: normal; 38 | color: #cb4b16; 39 | margin-top: 2.5em; 40 | } 41 | 42 | h1 { 43 | margin-top: 4.0em; 44 | } 45 | 46 | h3 { 47 | font-style: italic; 48 | } 49 | 50 | code { 51 | font-family: "Fira Mono", "Hack", "Noto Sans Mono", "DejaVu Sans Mono", monospace; 52 | font-size: 0.9em; 53 | font-weight: 400; 54 | } 55 | 56 | a { 57 | color: #268bd2; 58 | text-decoration-color: #7daed1; 59 | } 60 | 61 | a:hover, a:focus { 62 | color: #cb4b16; 63 | text-decoration-color: #cb4b16; 64 | } 65 | 66 | p { 67 | font-size: 1.25em; 68 | text-rendering: optimizeLegibility; 69 | } 70 | 71 | 72 | *:not(pre)>code { 73 | padding: 0.0em 0.35ex; 74 | background-color: #e5e7e8; 75 | border-radius: 6px; 76 | -webkit-border-radius: 6px; 77 | word-wrap: break-word; 78 | } 79 | 80 | pre.rouge { 81 | padding: 1.0em; 82 | } 83 | 84 | pre, pre>code { 85 | line-height: 1.30; 86 | font-family: "Fira Mono", "Hack", "Noto Sans Mono", "DejaVu Sans Mono", monospace; 87 | font-size: 1.0em; 88 | border-radius: 12px; 89 | -webkit-border-radius: 12px; 90 | } 91 | 92 | .admonitionblock td.icon [class^="fa icon-"] { 93 | font-size: 2.5em; 94 | text-shadow: 1px 1px 2px rgba(0, 0, 0, .5); 95 | } 96 | 97 | .admonitionblock>table td.icon { 98 | text-align: center; 99 | width: 80px; 100 | } 101 | 102 | .admonitionblock td.icon .icon-note:before { 103 | content: "\f05a"; 104 | color: #7b868a; 105 | } 106 | 107 | .admonitionblock td.icon .icon-warning:before { 108 | content: "\f071"; 109 | color: #cb4b16; 110 | } 111 | 112 | .literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { 113 | background: #eeebe2; 114 | padding: 10px; 115 | } 116 | 117 | .listingblock>.title { 118 | line-height: 1.0; 119 | font-size: 1.0em; 120 | color: #cb4b16; 121 | font-weight: 400; 122 | font-style: italic; 123 | margin-bottom: -0.5em; 124 | } 125 | 126 | .conum[data-value] { 127 | display: inline-block; 128 | color: #fcfaf7; 129 | background-color: #7b868a; 130 | border-radius: 5px; 131 | -webkit-border-radius: 5px; 132 | text-align: center; 133 | font-size: 0.8em; 134 | width: 3.0em; 135 | line-height: 1.5em; 136 | margin-right: 1em; 137 | font-style: normal; 138 | font-weight: bold; 139 | } 140 | 141 | hr { 142 | border: solid #7b868a; 143 | border-width: 1px 0 0; 144 | margin-top: 5em; 145 | } 146 | 147 | em, i { 148 | font-style: italic; 149 | line-height: inherit; 150 | } 151 | 152 | strong, b { 153 | font-weight: bold; 154 | line-height: inherit; 155 | } 156 | 157 | .conum[data-value]+b { 158 | display: none; 159 | } 160 | 161 | .conum[data-value]:after { 162 | content: attr(data-value) 163 | } 164 | 165 | pre .conum[data-value] { 166 | position: relative; 167 | top: -.125em; 168 | } 169 | 170 | b.conum * { 171 | color: inherit!important; 172 | } 173 | 174 | li p { 175 | margin-top: -0.5em; 176 | padding-left: 0.5em; 177 | } 178 | 179 | @media only screen and (min-width:850px) { 180 | body.toc2 { 181 | padding-left: 17em; 182 | margin-top: -5em; 183 | } 184 | 185 | #toc.toc2 { 186 | background: #fcfaf7; 187 | position: fixed; 188 | width: 17em; 189 | left: 0; 190 | top: 0; 191 | border-right: 2px solid #efefed; 192 | padding: 0.5em; 193 | height: 100%; 194 | overflow: auto; 195 | } 196 | } 197 | 198 | #toc.toc2 #toctitle { 199 | margin-top: 0; 200 | margin-bottom: .8rem; 201 | font-size: 1.25em; 202 | } 203 | #toc.toc2>ul { 204 | font-size: 0.875em; 205 | padding-left: 0.5em; 206 | } 207 | 208 | #toc.toc2 ul ul { 209 | padding-left: 0.5em; 210 | } 211 | 212 | #toc.toc2 a { 213 | text-decoration: none; 214 | } 215 | 216 | #toc.toc2 li { 217 | margin-left: 0.5em; 218 | margin-top: 0em; 219 | margin-bottom: 0em; 220 | text-align: left; 221 | } 222 | --------------------------------------------------------------------------------