-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial.html
More file actions
208 lines (200 loc) · 17.8 KB
/
tutorial.html
File metadata and controls
208 lines (200 loc) · 17.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat&family=Nanum+Gothic+Coding&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<title>Commander</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/xt256.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script>
<script>hljs.highlightAll();</script>
</head>
<body>
<div class="banner">
<a href="./" class="noHover"><img src="imgs/commander_logo.png" alt="The Commander
Logo" class="logo"></a>
<a href="./">About</a>
<a href="./team.html">The Team</a>
<a href="./download.html">Download</a>
<a href="./tutorial.html" class="active">Tutorial</a>
</div>
<div class="banner-mobile">
<a href="./" class="noHover"><img src="imgs/commander_logo_short.png" alt="The Commander
Logo" class="logo-mobile"></a>
<div class="drop-down">
<button class="drop-down-button" onclick="myFunction()">Menu</button>
<div id="dropdown" class="drop-down-content">
<a href="./">About</a>
<a href="./team.html">The Team</a>
<a href="./download.html">Download</a>
<a href="./tutorial.html">Tutorial</a>
</div>
</div>
</div>
<div class="content">
<h1>1. Getting Started</h1>
<p>First, download the executable for your system from <a href="download.html">downloads</a>. Alternatively, you may compile and build it yourself by downloading the source code from the <a href="https://github.com/Commander-Language/Commander/tree/main">repo</a> and compiling/building it from scratch. For this tutorial, we will assume you are using a Bash shell running in Linux to run the interpreter.</p>
<p>The intended way to run commander is by writing the source code in a file and running that file. You do this by running the following command (or similar command):
<pre class="command">
<code class="language-bash">
$ commander -f hello_world.cmdr
</code>
</pre>
The file "hello_world.cmdr" may look something like:
<pre class="code">
<code class="language-typescript">
println "Hello World!";
</code>
</pre>
This is just your usual "hello world" program though, so it's not very interesting. The next section of the tutorial we will create a more interesting program that will show case some of the features of Commander, and hopefully give you an idea of its general syntax highlighting and usage. We don't have any working syntax plugins at the moment, but if you want to have your code highlighted we would recommend setting your code editor to highlight Typescript since it is probably the most similar language in terms of syntax. In fact, if you already know some Javascript or Typescript, and also are familiar with Bash commands, picking up Commander should be pretty straight-forward.
</p>
<h1>2. A Basic Program: PokemonDiscover</h1>
<p>Do you like Pokemon? Do you struggle to keep up with all the new ones? If so, you may like this program we are going to create! This program, which we call "PokemonDiscover", utilizes a free <a href="https://pokeapi.co/">Pokemon API</a> online to download images and display them to the user. The idea is to go through all the Pokemon, and let the user discover any ones they didn't know about, and give them the option to save the image of the Pokemon to their computer so they can have a collection of all their favorites.</p>
<br><p>Before we begin, you will need to install a couple commands if you don't already have them (since they will be used in the program). The ones you might already have since they are sometimes pre-installed are the curl command (for making API calls), the tr command (for trimming command output), and wget (for downloading files from the internet). You will also need the jq command for parsing JSON, which may not be included in your distro by default, but you should be able to install it simply using your package manager. Similarly, you will need a program to display png images, of which there are many you can choose from. For this tutorial, we will use the feh command since it is pretty simple to install via the package manager and use, although you can also use ImageMagick's display command. These image programs will display in an outside window from the terminal however, so if you want the images display in the terminal, you may want to look for another program to use such as imgcat (although these aren't as straight-forward to set up).</p>
<p>To begin, let's introduce the user to the program and ask where they would like to store their pokemon:</p>
<pre class="code">
<code class="language-typescript">
println "Welcome to PokemonDiscover!";
directory = scan "Where do you want to store your Pokemon?: ";
mkdir -p $directory;
</code>
</pre>
<p>The scan statement reads in user input with a prompt. We then call the mkdir command with the -p flag to make all the necessary directories up to the one specified if they don't already exist. Note you can use variables in commands by prefacing the variable with a $ (similar to Bash).</p>
<p>Next, we will create a function that will get all the Pokemon from the API. That will look something like:</p>
<pre class="code">
<code class="language-typescript">
fn getAllPokemon(): string[] {
url: string = "https://pokeapi.co/api/v2/pokemon?limit=-1";
type command = (string, string, int);
response: command = `curl -s $url`;
output: string = response[0];
statusCode: int = response[2];
assert statusCode == 0, "Error occurred while running curl command";
assert output != "Not Found" && !output.startsWith("404"), "Url '$url' not found!";
return `echo $output | jq -r '.results[] | "\(.name) \(.url)"'`[0].split("\n");
}
</code>
</pre>
<p>This creates a function named "getAllPokemon" that returns a string array of all the Pokemon info. In this function we specify the types of the variables and the return type of the function explicitly, but it is completely optional to specify types. Specifying types will sometimes allow the type checker to be more strict in ensuring types are consistent throughout the program, so including them can be beneficial, but the rest of the tutorial we will not specify times to keep the code a little more simpler. To explain what the function does, let's walk through it line by line:</p>
<ol>
<li>The first line stores the url string into a variable called "url". We specify the type here to be a string.</li>
<li>This line specifies a new type called "command" which is a tuple containing two strings and an integer. Tuples are made using parentheses and can store multiple types unlike arrays which are made using square brackets and only store one type. This is the type returned when doing command substitution, which is done in the next line, but I explicitly define it here to make it obvious what we are doing.</li>
<li>This line stores the result of running the "curl -s $url" command that hits the API and returns the Pokemon data. The tuple returned contains the standard output, the standard error output, and the exit status code of the command, in that order. We don't care about the standard error, but we do care about the standard output and status code, so we store those into variables in the next two lines.</li>
<li>This line stores the standard output of the previous command into a variable named output.</li>
<li>This line stores the status code of the previous command into a variable named status.</li>
<li>This line uses an assertion to ensure that the status code of the curl command is 0 (i.e. success). If it isn't 0, it will display the error message provided and the program will terminate.</li>
<li>This line also uses an assertion, this time to ensure that the curl command was able to successfully hit the url endpoint, and that it didn't return an HTTP 404 error. If it wasn't found, the error message is displayed.</li>
<li>The last line returns the Pokemon JSON data objects as an array of strings. The command itself is a little complicated, but essentially it is running the jq command, piping in the output we previously got into it, and listing out all the name and ids of each Pokemon. Each name and id is on it's own line, so we split the standard output by new line characters to get the array of strings with each string representing the name/id pair of the Pokemon.</li>
</ol>
<p>Now we want to go through each Pokemon and display them to the user so they can decide whether to keep them or not. We will create the loop first, and put everything else into a helper method after:</p>
<pre class="code">
<code class="language-typescript">
pokemon = getAllPokemon();
for (i = 0; i < pokemon.length(); i++) {
pair = pokemon[i].split(" ");
processPokemon(pair[0], pair[1]);
}
println "That is all of them. Goodbye!";
</code>
</pre>
<p>In this code, we get all the Pokemon by calling our method we made. We then iterate over the list of strings, splitting each string into its name/id values, which then get passed into the "processPokemon" function. After looping, we say goodbye to the user since the program will then be finished executing.</p>
<p>Now, we want to implement the processPokemon function, and then we are done. Note that we will need to define this function before we begin the for loop; this is because the file is executed top to bottom, and so functions can only be called after they are defined. Additionally, this function takes in two parameters: the name of the pokemon, and the url endpoint to get its data. It should be noted that these parameters will be passed by reference since they are strings. In fact, all types will pass by reference with the exception of ints, floats, and bools. Here is the function implementation:</p>
<pre class="code">
<code class="language-typescript">
fn processPokemon(name, url) {
id = `curl -s $url | jq -r '.id' | tr -d '\n'`[0];
imageFile = "$directory/$id.png";
wget -qO $imageFile "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/$id.png";
feh $imageFile;
if ((scan "Do you want to keep $name? (y/n): ") == "n") {
rm $imageFile;
}
}
</code>
</pre>
<p>This function doesn't return anything, hence why there is no return statement. The return type would thus be "void" if we were specifying the return type, but we don't in the example. Again, let's walk through the code of this function:</p>
<ol>
<li>The first line, similar to how we did it in the "getAllPokemon" method, will make a curl request to the url passed in, and will pull the Pokemon's Pokedex id from the JSON object. The tr command is used here to trim off trailing whitespace from the id, but we could've also called the trim() function that's part of the string itself if we wanted to as well.</li>
<li>This line simply creates a new string representing the file path to the image file we are going to download. It uses string interpolation to insert the values of the variables into the string. This way of doing it only works with variables, but you can also do it with any expression by using syntax like "${directory}/${id}.png" or $"{directory}/{id}.png", where anything in-between the curly braces will be commander code that is run, so long as it is an expression that returns some value, such as an add or other math operation or a function call.</li>
<li>This line calls the wget command. The -q flag prevents it from printing out stuff (to avoid putting too much data in the console), and -O followed by the image file path to store the image in. The url points to an image of the Pokemon, and it knows which one to get since we use string interpolation to insert the id of the Pokemon into it. You may notice that the url is not part of the API, but despite that the url is contained in the JSON data for each Pokemon, so we could just as easily have gotten the url from there. We just explicitly wrote it here to make things a little more simple.</li>
<li>This line opens the image file and displays it to the user using the feh command.</li>
<li>This line is an if statement that asks the user if they want to keep the image. If they type in "n", then it will remove the image from their directory, otherwise it will leave it there.</li>
</ol>
<p>And that is it! You should be able to run it now and see it fully working. Feel free to practice more by adding more features to it.</p>
<h1>3. Other Features</h1>
<p>If you want a more in dpeth look at the different features that Commander has (the last program only scratches the surface!) then you will want to take a look at the <a href="https://github.com/Commander-Language/Commander/blob/main/documentation/spec.md">specification documentation</a> we have for the language contained in the <a href="https://github.com/Commander-Language/Commander/tree/main">Commander repo</a>.</p>
<p>Also, in addition to running scripts, there are a couple other features that are part of the interpreter.</p>
<p>First, you may run it as an interactive shell (or REPL) by running it by itself:
<pre class="command">
<code class="language-bash">
$ commander
</code>
</pre>
Using the shell, you can run the code directly line by line. You can run the shell with any of the flags detailed below as well, such as the transpiler flags.
</p>
<p>Second, you may transpile commander scripts into either Bash or Powershell scripts. At the moment, these transpilers are not complete, but they are partially implemented. To run the bash transpiler, provide the -b flag, such as in the following command:
<pre class="command">
<code class="language-bash">
$ commander -b -f hello_world.cmdr
</code>
</pre>
This will just print out the transpiled text though. If you want to save it to a file, you may provide the -o flag followed by the file name, such as follows:
<pre class="command">
<code class="language-bash">
$ commander -b -f hello_world.cmdr -o hello_world.sh
</code>
</pre>
To transpile to powershell, use the --powershell flag:
<pre class="command">
<code class="language-bash">
$ commander --powershell -f hello_world.cmdr
</code>
</pre>
</p>
<p>Third, and this is more of a debugging feature, you may run the interpreter such that it only runs the lexer, parser, or type checker so you can see how it processes the code in each stage. To run just the lexer to see the list of tokens generated, provide the -l flag:
<pre class="command">
<code class="language-bash">
$ commander -l -f hello_world.cmdr
</code>
</pre>
To run only the lexer and parser to see the sExpression tree generated, use the -p flag:
<pre class="command">
<code class="language-bash">
$ commander -p -f hello_world.cmdr
</code>
</pre>
To run only the lexer, parser, and type checker to see the sExpression tree generated with all the assigned types, use the -t flag:
<pre class="command">
<code class="language-bash">
$ commander -t -f hello_world.cmdr
</code>
</pre>
</p>
</div>
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("dropdown").classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.drop-down-button')) {
var dropdowns = document.getElementsByClassName("drop-down-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
</script>
</body>
</html>