Chapter 1: Cracking Your First Nut
Kelvin Shadewing's Den

CHAPTER 1: CRACKING YOUR FIRST NUT



Squirrel is an amazing programming language. Despite its youth and limited documentation, it's already being used in the professional field, and has been ported to a number of platforms. In this course, you'll be using a simple console-based Squirrel VM and a source file to practice using Squirrel in. Each lesson will come with exercises you can do to ensure you understand the concepts introduced. You can use this testbed to try out Squirrel code.


Before we begin, I should point out that some functions you'll be using are built into Squirrel, and some are defined by me. Don't worry about the VM's source code until later, we'll be looking at how to bind Squirrel to a C++ application later on. I should also mention that this tutorial was built after reviewing the Squirrel reference manual and experimenting with each feature. The documentation for Squirrel is very limited, and some of it is weird, so some information regarding Squirrel may well be missing from this guide. If you know something I don't, please feel free to comment below, and I'll add it in where appropriate. For now, let's start with the language itself.


LESSON 1.1: EXAMINING A .NUT FILE

Let's take a look at a simple example of a Squirrel function:

print("Hello world!");

This is the most basic code that will produce output. When the Squirrel VM compiles a file, it will automatically run all code in the file until it reaches the end or hits an error. When calling a function, it's done by typing the name of the function, followed by a pair of parentheses with values between them that are separated by commas. These values are called arguments. I'll cover more on functions in the next section.

After every statement, you will place a semicolon ;. This let's the VM know that a statement or block (code within curly braces {...}) has ended.

EXERCISE 1.1

Download SquirrelVM.zip, if you haven't already, and unzip it, then open "test.nut" with any text editor. Enter the code above into this nut and run it. You can copy and paste, but typing it in yourself will help you to memorize it. The console should display "Hello world!" Change the text in quotes to whatever you want, and then continue.


LESSON 1.2: ASSIGNING VARIABLES

If you've ever done algebra, you've probably heard of variables. You know, the letters that got mixed up into the numbers that you were sure you'd never use in real life? Well, you're about to use them now, and in real life, too! Variables are values that can change at any time, as opposed to constants, which never change. They can come in many types, such as integers (whole numbers such as 1, 2, 3), floats (numbers with decimal values, such as 3.14159), strings (such as "This is a string.") and so on.

Squirrel is a dynamically typed language, meaning its variables can not only change in value, but can hold different kinds of values as well. For instance, a variable that was once holding the number 4 could be reassigned the string "four".

Now let's look at some more code.

local sHello; //I like to initial my variables with the type they're intended for to keep track; in this case, the letter "s" for "string".

sHello = "Hello world!";

print(sHello);

Using the local keyword, we can create a new variable. local places the variable within the current scope or pair of curly braces, and is added to what's known as the stack. This means that when the program leaves the scope, the variable will be deleted. Variables declared outside of a scope can be accessed from within it, provided a variable of the same name does not exist inside, otherwise the inner variable will be called instead. Those that are declared outside of all scopes are called global variables.

Another way to declare a variable is with the insert operator <-. To do this, you would name a variable followed by its value, like so: iAge <- 24;. The insert operator can be used to add variables to other scopes, such as tables, and is used to declare global variables, as all globals are stored on the global table. To access another scope, you would call it's name, followed by two colons, and then the statement. For instance:

::title <- "Hello world!";

{
	print(title);
}

Remember how I said variables out of scope will be overridden by those in scope? If you want to make sure you're using an outside variable, just include the scope name with colons, or namespace.

The operators you can use to assign values are as follows:

= sets the value to equal the expression to the right.
+= adds the expression to the right to the variable to the left.
-= subtracts the expression to the right from the variable to the left.
*= multiplies the variable to the left by the expression to the right.
/= divides the variable to the left by the expression to the right.
%= makes the variable to the left equal the remainder it would have if divided by the expression on the right.

Now, it is possible to use these same math operators on a variable and assign the solution to itself, like so: a = a + 5. But this causes some overhead or lag in your program. When using the joined operators shown above, you are directly altering a variable by the value to the right. When assigning a variable to itself with a math operation to the right, the system has to copy that variable's value to a new memory slot, add/subtract/etc. the number to that slot, then assign the new value to the variable and delete the temporary slot. So while it looks neater, it also serves a functional purpose.

Variables also have functions built into them (because everything you make in Squirrel is actually an object, which we'll learn more about later) that can be called the same way print() is, but with a difference: you call them by using the variable or value followed by a dot, then the function, though this does not work with numbers. Take a look at a few examples:

local thing = 25;

print(thing.tostring());

thing = 1.234;

print(thing.tointeger());

thing = "5.001";

print(thing.tofloat());

thing = "Don't print me! I'll cause an error!"

//print(thing.tointeger());

print("64".tointeger());

//print(25.tostring());

Run the code and see what these functions do to the values passed to them. Uncomment the disabled calls to print() to see the errors they produce.

If you want to convert a numeric literal into a string, you would have to wrap the literal in parentheses before calling the tostring() function. By using parentheses, you tell Squirrel that that's all there is, and that the dot that follows is not part of the number. When Squirrel sees a dot touching a number, it assumes it's meant to be a decimal point, and throws an error if anything other than a number follows it.

EXERCISE 1.2

Declare a local version of title in the same scope as the print statement, and give it a value of "Goodbye space!", then run it. The output should be Goodbye space! instead of Hello world!.


LESSON 1.3: COMMENTS

Comments are a very simple concept, so I don't know why I waited until now to introduce them. A comment is a line of text that is ignored when source code is compiled into binary code. Comments are used to help make code easier to read by explaining in plain English (assuming you normally use English) what a piece of code does. It can also be used to turn off code without deleting it.

Single-line comments begin with a double forward slash, and end when the line in the file ends. Multi-line comments begin with a slash-star, and don't end until they reach a star-slash.

//This is a single-line comment.

/*This is a multi-line comment.
It works on multiple lines.*/

EXERCISE 1.3

Using the code from exercise 1-2, comment out the local title. The new output should be Hello world!. You can use either comment method you choose.


LESSON 1.4: ARRAYS

Sometimes you need to store more than one value in a variable. For this, we will use an array, which stores multiple indexed elements with a number for each one. The index numbers start counting from zero, so the first element in an array would be numbered zero, the second being one, third being two, etc.. Array elements are accessed using square braces []. Let's take a look at our print code using arrays.

local aWords = ["Hello", "Goodbye", "Code", "Squirrel", "Stuff"];

print(aWords[3]);

Notice how the word "Squirrel" is fourth in the array, but is called using the number 3.

Now let's get into more tricky business: multi-dimensional arrays. Like JavaScripte, Squirrel does not support these natively, so to make a two-dimensional array, it would look like this:

local twodee = [[1, 2, 3, 4],
["one", "two", "three", "four"],
["uno", "dos", "tres", "quatro"]];

print(twodee[1][2]);

This code will output dos because it's in the second column (accessed with 1) and the third row (accessed with 2). The first dimension index determines the column in a 2-dimensional array, and the second index determines the row. Imagine it being a grid that starts at the top left, and the indices tell how far right, then how far down to go.

Because of the way this works, it's possible to make arrays with mismatched lengths to each first-dimensional element. These are called jagged arrays. Because Squirrel supports jagged arrays, it is also possible to make an array with many dimensions without multiplying the ammount of space they take up by allocating numerous empty slots.

There are some functions you can use to manipulate arrays. These are called by joining them to the array's name with a dot like this: arrayName.functionName();. Don't worry about memorizing all these at once. Just come back to this page when you need a refresher. They are as follows:

len() gives the length of an array.
push(val) / append(val) adds a new value to the end of an array.
extend(array) adds another array to the end of the one that called it.
pop() returns a value at the end of an array, then deletes that value. You'll learn about return values in section 3.
top() does the same as pop(), but without deleting the element.
insert(idx, val) inserts a value at position idx.
remove(idx) deletes the value at position idx.
resize(size, [fill]) changes the size of an array to fit size. If fill is defined, all new elements will equal that, otherwise they'll be zero.
reverse() reverses the order of all elements.
sort(compare_func) sorts all elements in an array. A custom sorting function can be defined. See the Squirrel Reference Manual for more information.
slice(start, [end]) returns a section of an array from start to end. If end is not defined, it will go to the end of the array.
weakref() returns a weak reference to the object. We'll be learning about those later.
tostring() returns the string (array : pointer) where pointer is the array's address in memory.
clear() empties the array.
map(func(a)) returns a new array. If func(a) is defined, it will be executed with the return value being assigned to the new element.
apply(func(a)) assigns each element in the array to the return value of the function.
find(val) searches the array until it finds an element with that value, then returns the element's index.
filter(func(idx, val)) creates a new array with all elements that pass the test implemented by the provided function. In detail, it creates a new array, for each element in the original array invokes the specified function passing the index of the element and it's value; if the function returns true, then the value of the corresponding element is added on the newly created array.

EXERCISE 1.4

Experiment with any of the array functions you want to and see what you get. Try at least three of them.

LESSON 1.5: TABLES

Like arrays, tables are a way to store multiple values in a single variable. Unlike arrays, however, which have numbered elements, tables have additional variables stored within them.

local foo = {};

foo.d <- "Pizza";

print(foo.d);
print(foo["d"]);

Here we can see the insert operator is used to create elements within the table. From there, they can be accessed the same way they were declared, and can be assigned new values with =. However, using the equal sign to assign a value to a slot that doesn't exist will throw an exception (meaning the program will crash).

Table variables can be accessed using dots, or by using array notation with the element being specified with a string instead of a number.

EXERCISE 1.5

Add new slots to foo. Make one of them an array and print all the elements in the array.


LESSON 1.6: MORE WITH STRINGS

You may have noticed that the output for your array in exercise 1.5 was all jumbled together as one word. There is a way to fix this. It is possible to add strings together using the plus sign, even with numbers; adding a number and a string will always return a string.

local foo = "This" + " " + "That";

print(foo);

The output here will be This That. Remember that adding a string will always return a string. If you add "45" to 45, you will end up with "4545".

Another important thing to understand is the concept of escape sequences. These are characters preceeded by a backslash \ that tells the string to insert a special character that can't normally be typed. Some escape sequences include the following:

\n adds a new line.
\t adds a tab space.
\\" adds a quotation mark.
\\ adds a backslash.

EXERCISE 1.6

Go back to the code in exercise 1.5 and rewrite the print statement(s) so the array elements in foo will show up separately, each on a new line.


LESSON 1.7: CONSTANTS AND ENUMERATIONS

Sometimes you want to assign names to values without them being able to change. This is where constants come in. As you may remember from algebra, a constant is a literal value that does not change. In programming, it is similar to a variable, in that it has a name, address, and other properties variables have, but still does not change in value once defined. They are defined at the same time a source file is compiled, and so it is impossible to change them; it's as though you'd written a number or string over and over throughout your code.

The reason why constants exist is so you can easily make changes to numerous areas in your code by only altering one piece of actual code. For instance, say you decide to put a level cap on all the characters in your game, and then make a ton of characters, only to decide later that the cap needs to be changed. By having a constant represent all these level caps at once, you can change them all just be rewriting your constant.

A constant is defined the same way a variable is, but using const instead of local.

An enumeration is a list of constants that increments with every next value, starting with zero. It is defined as such:

enum numbers{
	zero,
	one,
	two,
	three,
	four
};

Enumerations can also have values assigned to them, making them work much like a table does.

enum types{
	integer = 12,
	float = 4.5,
	string = "This"
};

EXERCISE 1.7

Create a constant, and try to change it. Create an enumeration with some values being defined and others not. Do you get errors? Continue to experiment with these before moving on.


CHAPTER 1 REVIEW

Using what you've learned from this section, write a nut that has a variable storing your name along with the names of your friends in a two-dimensional array. In the second column of the array, give each person an age. Print each person on their own line with their age next to their name, separated by a tab space. In the third column, add a quote for each person, and surround it in quotation marks. Print these quotes onto the respective lines with who they're for.


That's it for this section! When outputting foo()'s contents, you may have used the print() function multiple times. The next section will show you some shortcuts for dealing with repetative code.

Are you enjoying this tutorial? If so, why not send a donation? You can do so by clicking the donate link at the top of the screen, and if you do, please attach a note to your payment letting me know it's for the tutorial!

NEXT >>