Simba uses Lape.
What is Lape? Taken directly from the Lape repository:
""Lape is a scripting engine with a Pascal derived syntax for Free Pascal and Delphi. Itâs written with speed and a broad platform compatibility in mind. The syntax is backwards compatible with Pascal Script (to a certain degree).""
So the syntax is basically Free Pascal, whenever you have a question about syntax, math operations or something low level like that, if you canât find your answer in the tutorials you should check the FPC Manual: https://www.freepascal.org/docs-html/current/ref/ref.html
First we are going to talk about variables. In Lape you have Constants and Variables. Both are a way of storing values. The difference between the two is that constants cannot be changed once declared.
Declaring constants:
const
CONSTANT_STRING: String = 'Hello world';
CONSTANT_INT: Int32 = 5;
Declaring variables:
var
VariableString: String := 'Hello world';
VariableInt: Int32 := 5;
Variables can also be declared with no value and be assigned one at a later point:
var
VariableString: String;
VariableInt: Int32;
begin
VariableString := 'Hello world';
VariableInt := 5;
end.
You can also declare multiple variables of the same type this way:
var
Str1, Str2, Str3: String;
VariableInt1, VariableInt2: Int32;
Also, FPC is not case sensitive, so if you wrote the constants code block like this:
cOnSt
consTant_StRing: StrINg = 'Hello world';
coNStaNT_inT: iNt32 = 5;
It would be function exactly the same way, but itâs a good practice to make constants uppercased with underscores separating words in itâs name while variables are usually written CamelCased.
This improves readability greatly and makes it easier for you to maintain as well others helping you.
Itâs also commonly accepted that words that Simba displays in bold, which are often reserved words, should be written in lower case.
Some example of such words are:
const
var
type
for
do
with
while
repeat
until
case
```And it also doesn't matter if many spaces or new lines you use:
```pascal
var VariableString: String := 'Hello world' ; VariableInt: Int32; begin VariableInt := 5; end;
But itâs usually a best practice to make a new line and indent it after a reserved word.
Commenting your code:
(*
This is a multilined
comment block.
*)
//This is a single line comment.
var
VariableInt: Int32; //Single line comments can be used after a line of code.
Code blocks should be wrapped with:
begin
//This is a code block.
end
If statements can execute either a code block or a single line:
var
VariableString: String;
BooleanVariable: Boolean;
begin
BooleanVariable := True;
if BooleanVariable then
begin
VariableString := 'Hello world';
Writeln(VariableString);
end;
BooleanVariable := False;
if not BooleanVariable then //not negates a statement.
Writeln('The statement was false');
end.
else statements must always be preceded by a code block of a previous if statement:
var
VariableString: String;
IntVariable: Int32;
begin
IntVariable := 1;
if IntVariable = 0 then
begin
VariableString := 'Hello world';
Writeln(VariableString);
end
else if IntVariable = 1 then
Writeln('The statement was false')
else
Writeln('IntVariable is neither 0 nor 1');
end.
if/else statements can also be single line like everything else:
if True then Writeln('The statement is True')
else Writeln('The statement is False');
Semicolons mean the end of a code block/line while a period means the end of your script:
begin
//code block
end;
begin
//FINAL CODE BLOCK
end.
begin
//This will never run because the script ends as soon as it finds the period.
end;
So knowing this and knowing else statements have to be preceeded by a code block/line of a previous if statement, you know you canât do this:
var
VariableString: String;
BooleanVariable: Boolean;
begin
BooleanVariable := True;
if True then
begin
VariableString := 'Hello world';
Writeln(VariableString);
end; //pay attention to the semicolor.
else if False then
Writeln('The statement was false');
end.
The semicolon would end the if statement there and would leave the else statement in the next line without a matching previous if statement.
You have 3 types of loops.
for loops; while loops; repeat until loops;
The easiest one to understand is the while loop:
while True do
begin
//this will loop forever because the statement is an unchangeable True.
end;
While loops can also take statements as long as they have a boolean result (either True
or False
):
while 1 > 0 then
Writeln('Thsi will loop forever because 1 is always bigger than 0'); //for single lines there's no need for begin and end;
While loops can also use a boolean variable:
BooleanVariable := True;
while BooleanVariable then
begin
BooleanVariable := False; //This will make BooleanVariable false and therefor the loop will only run once.
end;
Repeat Until loops are extremely similar, but take the statement at the end:
repeat
Writeln('This will only run once as 1 is greater than 0.');
until 1 > 0;
This guarantees type of loops are almost functionally identical to while loops but guarantee that the loop contents will run at least once:
BooleanVariable := False;
while BooleanVariable then
Writeln('This will never run because BooleanVariable is False');
repeat
Writeln('This will run once because the statement is only asked after this was run.');
Writeln('also, repeat/until function as a code block and you can do several lines without being/end');
until BooleanVariable;
For loops are slightly harder to wrap your head around, specially if you are new to programming. But basically you are iterating a loop X amount of times until you reach a ""goal"".
Example:
var
i: Int32; //a lower case i is often used to represent Iteration or Index.
begin
for i := 0 to 50 do //i := 0 sets the i variable to 0 at the start of the for loop. to 50 means we want to loop it until i = 50.
begin
Writeln('i is currently at: ', i); //this will print i at each loop.
//i will be incremented by 1 after each loop.
end;
end.
You can also decrement instead of incrementing with:
var
i: Int32;
begin
for i := 50 downto 0 do //i := 50 sets the i variable to 50 at the start of the for loop. downto 0 means we want to loop it until i = 0.
Writeln('i is currently at: ', i); //this will print i at each loop. i will be decremented by 1 after each loop.
end.
The keywords here are to
and downto
.
You also have the keyword in
that is used in arrays but Iâll explain that will be explained later.
Regardless of the loop you are using (for loop, while loop, repeat until loop) you can always break out of the loop prematurely with the keyword Break
:
var
i: Int32;
begin
for i := 0 to 50 do
begin
if i >= 30 then
Break; //Once i >= 30 we will break out of this loop.
Writeln('i is currently at: ', i); //this will print i at each loop.
end;
end.
You can also skip part of the loop code block with the keyword Continue
:
var i: Int32;
begin
for i := 0 to 50 do
begin
Writeln('i is currently at: ', i);
if i >= 30 then Continue; //Once i >= 30 we will end this iteration of the loop and continue with the next one.
Writeln('This text will be printed until i >= 30');
end;
end.
Arrays are also something that can be hard to understand for people new to programming. Basically, an array is a ""collection"" of a type of variable. For example:
var StringArray: TStringArray := ['String1', 'String2', 'String3'];
They can also take variables of the same type:
var
Str: String := 'String4';
StringArray: TStringArray := ['String1', 'String2', 'String3', Str];
If you need a type of array that doesnât exist already you can create it like so:
type
MyCustomArray: array of String; //We already TStringArray which is equivalent to this CustomArray but I'm using string
//since it's a variable we already learned about.
var StringArray: MyCustomArray:= ['String1', 'String2', 'String3'];
To use the variables stored in the array you do it like so:
var
IntArray: TIntegerArray:= [1, 5, 3];
Result: Int32;
begin
Result := IntArray[0] + IntArray[2]; //This will sum the int at index 0 of the array with the int at index 2 of the array. (1 + 3).
Writeln(Result); //This will print 4.
end.
You can also modify the contents of the array like so:
var
IntArray: TIntegerArray:= [1, 5, 3];
begin
IntArray[0] := 5;
IntArray[1] += 2; //will sum 2 to the current IntArray[1].
Writeln(IntArray[0]); //This will print 5.
end.
You can also modify the array:
var IntArray: TIntegerArray:= [1, 5, 3];
begin
IntArray := [0, 1, 2, 3, 4]; //This assigns this new array to IntArray.
Writeln(IntArray[0]); //This will print 0.
end.
For loops are usually very handy to handle arrays. You can use for loops with arrays in this two ways:
var
i: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
begin
for i := Low(IntArray) to High(IntArray) do //Low(Array) and High(Array) gives you the Lowest/Highest index of an array.
Writeln(IntArray[i]); //This will print the int at i index of IntArray.
for i := 0 to High(IntArray) do //Because arrays always start at index 0 you can just do i := 0.
Writeln(IntArray[i]);
end.
You can also use the in
keyword:
var
i: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
Str: String;
StrArray: TStringArray := ['Str1', 'Str2', 'Str3'];
begin
for i in IntArray do //what this means is: for each i (Int32) in IntArray (TIntegerArray) do...
Writeln(i); //This will print each i.
for Str in StrArray do //Strings might be easier to undersand the in keyword.
Writeln(Str); //for each string in the string array do writeln(string).
end.
You can also use the for loop to add values to an array:
var
i: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
begin
for i := 0 to 2 do
IntArray += i; //This will append i to the IntArray on each loop.
Writeln(IntArray); //This will print: [5, 2, 3, 7, 0, 1, 2]
end.
You can also modify the array contents with a for loop as such:
var
i: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
begin
for i := 0 to High(IntArray) do
IntArray[i] += 3; //This will sum 3 to i index of IntArray.
Writeln(IntArray); //This will print: [8, 5, 6, 10]
end.
But the following doesnât work:
var
i: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
begin
for i in IntArray do
i += 3; //This will sum 3 to i index of IntArray.
Writeln(IntArray); //This will print: [5, 2, 3, 7]
end.
I donât know very well how to explain why, but the way I understand it is that when you use the in
keyword you are not actually using the variable in the array but a copy of it instead.
So functionally it sort of works like this in the background:
var
i, TempInt: Int32;
IntArray: TIntegerArray := [5, 2, 3, 7];
begin
for i := Low(IntArray) to High(IntArray) do
begin
TempInt := IntArray[i];
TempInt += 3; //So you are modifying the temporary variable and not doing anything with it.
end;
Writeln(IntArray); //This will print: [5, 2, 3, 7]
end.
Now letâs talk about functions in Lape. For people new to programming, functions are a piece of code you write that can later be called where you desire.
This is useful if you have to use the same exact 20 lines of code 5 times for example. Without a function that would take you 100 lines.
With a function you just write the code once and then call it 5 times doing with 25 lines what would require 100 otherwise.
Lape, has two type of functions:
Procedures: This simply execute the code contained in them. Functions: This execute the code inside of them and return a result. The result can be of any type you want.
They are usually CamelCased and when itâs some kind of helper function sometimes itâs preceded with an underscore.
Procedures are pretty straight forward:
procedure MyProcedure; //Declaring the procedure.
begin
//Your code.
end;
begin
MyProcedure; //Calls the procedure.
MyProcedure; //Calls it again.
MyProcedure; //Calls it again.
end.
Functions are not that hard to understand either but they are more complex:
function MyFunction: Boolean; //Declaring the function. In this case, the result of the function will be a boolean,
begin //but can be a string, an int or any other type.
//Your code.
Result := True; //Always set a result or Exit a value (more on that later) otherwise just use a procedure!
end;
procedure MyProcedure;
begin
//Your code.
end;
begin
if MyFunction then
MyProcedure; //Calls the procedure if MyFunction returns true.
end.
Procedures and Functions can also take in Parameters of any type you want and you can also use them inside each others:
var
JoinedStr: String;
function StringJoiner(Str1, Str2: String): String; //We pass to the function Str1 and Str2 and this time the result will be a String.
begin
Result := Str1 + Str2;
end;
procedure MyProcedure(Str: String);
begin
JoinedStr := StringJoiner('Hello', Str);
end;
begin
MyProcedure('World');
Writeln(JoinedStr); //This will print 'HelloWorld'.
end.
Lastly, Iâm going to speak about records. This is slightly more advanced but I think itâs useful to at least know what they are so I make more sense in the next tutorials. Basically, a record is bunch of variables bundled together. Sort of like an object in other languages. For example:
type
TRSScript = record //record names in pascal are usually preceded by a T. in SRL, TRS means TRuneScape.
Name: String;
Version: Int32;
end;
We can then assign this record to a variable and use itâs build in variables:
type
TRSScript = record //record names in pascal are usually preceded by a T. in SRL, TRS means TRuneScape.
Name: String;
Version: Double;
end;
var
Script: TRSScript;
begin
Script.Name := 'My script';
Script.Version := 3.2;
end.
We can also make procedures and functions that are specific to this record. For example:
type
TRSScript = record //record names in pascal are usually preceded by a T. in SRL, TRS means TRuneScape.
Name: String;
Version: Double;
end;
procedure TRSScript.Setup;
begin
Self.Name := 'My script'; //Self can be omitted most times. I usually omit it, though it's a good practice to put it in.
Self.Version := 3.2;
end;
var
Script: TRSScript;
begin
Writeln(Script.Name); //will print a blank line.
Script.Setup; //Runs the Script.Setup function.
Writeln(Script.Name); //will print 'My script'
end.
You can also have multiple instances of the record and that can be very useful:
type
TRSScript = record
Name: String;
Version: Double;
end;
TRSScriptArray = array of TRSScript; //Just thought I would throw this in here to show you can also make a custom array of your records.
procedure TRSScript.Setup(ScriptName: String; ScriptVersion: Double);
begin
Self.Name := ScriptName;
Self.Version := ScriptVersion;
end;
var
Script: TRSScript;
Script1: TRSScript;
Script2: TRSScript;
Script3: TRSScript;
ScriptArray: TRSScriptArray;
begin
Script1.Setup('First script', 3.2);
Script2.Setup('Second script', 1.2);
Script3.Setup('Third script', 0.1);
ScriptArray := [Script1, Script2, Script3];
for Script in ScriptArray do
Writeln('This is the ', Script.Name, ' with version number ', Script.Version);
end.
And with this I conclude this tutorial. If I remember something important Iâve missed Iâll update this guide a bit but for the most part this should cover most of the syntax. Whatever else you need I recommend you check the FPC Manual.