Lape syntax

Learn about Lape (Simba programming language) and it's syntax.

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:
```pas
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
  Boolean := 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('Thsi will loop forever because 1 is always bigger than 0');
until 1 > 0;

This guarantees type of loops are almost functionally identical to while loops but guarantee that the loop contentes will run at least once:

BooleanVariable := False;

while BooleanVariable then
  Writeln('This will never run because BooleanVariable is False'); 

repeat
  Writeln('Thsi 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 istead. 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.