Tutorial 1: Monster Basics
Table of Contents
- 1 Introduction
- 2 Saying Hello
- 2.1 Troubleshooting
- 3 Dissecting the example
- 4 The 'mvm' program
- 4.1 Other 'mvm' functions
- 5 Variables
- 5.1 Variable types
- 5.2 Initialization
- 6 Local variables and class members
- 7 More on functions
- 7.1 Parameters
- 7.2 Return statements and return values
Introduction
This tutorial is meant as a gentle introduction to the Monster scripting language. If you want to know more about Monster, see the about page. In this tutorial we will cover the language basics, like how to create and run scripts, the basic syntax and how to declare variables and functions. I will assume you have some prior experience with other programming languages.
To run the examples in this tutorial you will have to install the library and tools. I will write an in-depth installation guide for these in the near future; in the meanwhile you can follow the instructions on the download page and in the included README file.
Saying Hello
Let us begin by writing a simple script program and running it. Open a new file called myclass.m and copy the following text into it:
// Name this class class MyClass; // Used for writing text to the console native writeln(char[][] arr ...); // Program entry point main() { // Greet the world writeln("Hello World!"); }
Run the file with the following command line:
mvm myclass.m
The output should be (you guessed it):
Hello World!
Troubleshooting
If you get an error message of the sort
Class name XXX does not match file name yyy.m
make sure that the class name matches the name of the script file (except the case). If you named your class "DuckHunt" for example, try saving it as duckhunt.m. More on this in the next section.
Dissecting the example
The very first line of this script (starting with "//") is a comment, and is ignored. There are three types of comments:
// line comment: ignore the rest of the line /* block comment: ignore a block of text that can span multiple lines */ /+ nested block comments: ignore blocks of text, including other /* block comments */ /+ nested comments +/ /+ You /+ can /+ nest /+ these /+ as +/ deep +/ as +/ you +/ like +/ +/The nested comments are very useful for commenting out large blocks of code that contain other comments. The next line of the script is:
class MyClass;
This line declares the class name. In Monster, everything you declare is part of a class. If you are not familiar with the consept of classes, just think of it as a structure that contains everything we declare in this file. We will return the object oriented aspects of Monster in a later tutorial.
All Monster source files contain exactly one class, with the class name matching the filename, except for the .m extension. This scheme makes it easy for both humans and software to find any class when needed. This matching is not case sensitive though, so it is OK to use a lowercase file name (for example myclass.m) with a cased class name (MyClass). It is an error to have two class names in the same directory that differ only by case. This is especially important for portability, since some file systems (most Unix) are case sensitive while others (Windows) are not.
The next line:
// Used for writing text to the console native writeln(char[][] arr ...);
This declares a function called writeln. It is a native function, which means that the function body is not declared in Monster script itself. When you call writeln, you are actually calling a function inside the 'mvm' program. Native functions are one of the ways Monster code can communicate with the outside world.
Let us ignore the details of writeln for now, and move to the next function:
// Program entry point main() { // Greet the world writeln("Hello World!"); }
Here is our first "real" Monster function. The function called main is the 'entry point' of our program, and the program terminates when main is finished. A function body is declared inside C-style curly brackets "{" and "}". Here we write "Hello World!" to the screen and exit the program.
The 'mvm' program
You invoked the script with the following command:mvm myclass.m
Mvm is short for Monster Virtual Machine. Monster is a bytecode compiled language. This means that when you run a script file, it is first passed through a compiler, which checks the grammar and validity of your code. The compiler converts the code into a special set of instructions called bytecode, which can only be executed by a another program called the virtual machine (or VM).
The mvm program runs the script through the compiler and the VM for you. However, it is really only intended for running small examples. In a real-world application (like a game), a much better way to run scripts is to link the VM (which is a library) directly into your application, and running the scripts there. This gives you much more control over how scripts are run, and it lets you closely connect the script code with your program code.
If you only want to demonstrate simple language features though (like in this tutorial!), then mvm is perfect. We will stick with it through this tutorial and the next, and cover the more advanced methods later on.
The 'main' function we used above is a special feature of mvm, just like writeln. It is not a hard coded language feature like in C family languages. When we later build our own stand-alone applications, they will work without a main function.
Other 'mvm' functions
As we mentioned above, the writeln function is hard coded into the mvm program. Mvm also provides a few other functions that we can use, here are some of them:
| Name | Parameters | Function |
| write | char[][] args... | write output to console without a newline |
| writeln | char[][] args... | write output with a newline |
| writes | char[][] args... | like write, but separate all parameters with spaces |
| writelns | char[][] args... | separate with spaces and append a newline |
| exit | int code | exit the program immediately, with the given error code |
To use these, declare them like we did above and remember to mark them as 'native'. The writeln functions are very flexible. They will happily print numbers, strings and other types, and you can pass them any number of arguements. Some examples:
write(1, 2); // Write without changing the line writeln("3"); // Write a string writeln(); // Write an empty line writelns(4.4, "5.5", 6.6); // Separate parameters with spacesOutput:
123 4.4 5.5 6.6
Variables
Variables are used to store pieces of data. They are declared with C-like syntax:
int i; // An integer called i float f; // A floating point number called f
You can declare multiple variables with the same type:
int i, j, k; // i, j, and k are integers
Variable types
Monster is a strongly typed language. That means you must define the type of a variable before you use it. The basic types in Monster are:
| int | integer (32 bit signed) |
| float | floating point number (32 bit) |
| char | a single character (with full Unicode support) |
| bool | boolean value, can only have the values true and false |
More basic types will likely be added to the language in the future. There are other types as well, like arrays and objects, and we will return to those in another tutorial. Text strings are handled entirely as arrays of characters.
Initialization
You can initialize variables when you declare them:
int i = 1; float f = -2.5; char ch = 'M'; bool b = true; writelns(i, f, ch, b);
Output:
1 -2.5 M true
If you do not specify an initializer, variables are initialized to a default value. Unlike in languages like C, you can always safely assume that variables are initialized in Monster. The default value depends on the type:
| Type | Initializer |
| int | 0 |
| float | NaN (not a number) |
| char | 0x0000FFFF (invalid Unicode character) |
| bool | false |
The initial values are supposed to represent "illegal" values where possible, values that you would never use in practice (like NaN for floats). The intention behind this is to catch bugs where someone forgets to initialize a variable, and make these bugs become obvious as quickly as possible. Ints and bools have no "illegal" value, so they are simply initialized to zero or false.
int i; float f; char c; bool b; writelns(i, f, c, b);
Output:
0 nan false
Local variables and class members
Variable scope rules are (mostly) the same as in C-like languages. The scope of a variable defines where that variable is visible. There are two main types of variables: class variables (also called class members) and local variables, and these have different scope.
Local variables are declared inside functions, at the function scope. They are only visible inside that function, and they can only be used after the point where they were declared.
Class members are variables that are defined outside any function, at the class scope. They are visible from all functions in the class, and they are also visible from other classes and objects. All instances (objects) of a class have their own copy of the variable, just like in other object oriented languages.
A great rule of thumb in Monster (like in C), is that the scope of a variable or a function usually stays inside the set of curly brackets {} where it was declared. So, anything defined inside a set of curly brackets is usually not visible outside it.
A()
{
local = 2; // Error, 'local' is not declared yet
int local; // Defines a local variable called 'local'
local = 3; // Ok
member = 1; // Ok, 'member' is a class variable
}
// Defines a class variable called 'member'
int member;
B()
{
member = 4; // Ok
local = 5; // Error, local not declared in this scope
}
More on functions
We have already seen the functions writeln and main. These are predefined functions in the mvm program, as we discovered above. We can add our own functions to the class too, of course:
// New function func() { writeln(123, "ABC"); }
We can call it anywhere, just like we call writeln:
main()
{
// Call it a couple of times
func();
writeln("Hello World!");
func();
}
The output is:
123ABC Hello World! 123ABC
Can we call main this way too? Sure we can! Let us try this:
main()
{
main(); // Infinite recursion!
}
This will result in main calling itself in an infinite loop. The VM will catch up on this, and abort the code with an error message:
MonsterException: Function stack overflow - infinite recursion?
Parameters
You can pass values to your functions as well. Function parameters (or arguments) are declared as a comma separated list of variables:
main()
{
writeSum(1, 3);
writeSum(-2.5, 10.8);
}
// Write the sum of a an b
writeSum(float a, float b)
{
writeln(a, " + ", b, " = ", a+b);
}
Output:
1 + 3 = 4 -2.5 + 10.8 = 8.3
Parameters are treated as normal local variables inside the function. You can alter them, but the changes will only affect the local variable inside your function.
changer(int i) { i = 4; // only affects the local variable 'i' writeln("Inside changer: ", i); // prints 4 } main() { int number = 3; changer(number); writeln("In main: ", number); // 'number' is still 3 }
Output:
Inside changer: 4 In main: 3
Return statements and return values
You can exit a function before it reaches the end with the return statement.
func()
{
return; // exit the function
writeln("This is never exectued.");
}
You can also use 'return' to return a value from the function. To do this, you must first declare the return type of the function like this:
// Calculate the sum of a and b float sum(float a, float b) { return a+b; }
You can then use 'sum' in expressions like it was a float value.
float f = sum(1,2); // f = 3 writeln(sum(2,-1)); // writes 1
That's all for now!
I will return later with tutorials on conditional statements, arrays, loops, objects, states and virtual threads. I will also go into depth on how to create stand-alone programs using Monster, and how to integrate the VM into an existing program.
If you have any comments, suggestions or corrections, feel free to drop me a mail at the address below or leave a message in the forum!
-- Nico
korslund@gmail.com
Last updated: 2008-may-09