Lore And Lutes Object Oriented Programming Language Documentation For C++ Game Developers Vs. 1.01 (10/27/2002)

Written By: Andy Stone
ASFC By: Andy Stone (astone42@attbi.com) Visit: http://loreandlutes.sourceforge.net for more information. See copying.txt or copying for licensing information.


Changes (Document Versions):

1.0: Initial document.
1.01: Fixed some dumb spelling mistakes, re-worded a few things so they

sound better.

What LOOP Is:

The Lore And Lutes Object Oriented Programming Language (LOOP) is a library which includes code made to be modified into a scripting language. If you've ever edited a Lore And Lutes (L&L) game then you've used LOOP when you edit a class. This document describes how to use the LOOP library to create your own scripting language for games written in C++.

LOOP Dependencies:

LOOP depends on ASFC, and thus SDL and SDL_image, you'll need to have these libraries running and set up for development on your system before you can start programming with LOOP. Also I suggest that you read up on ASFC before trying to delve into the world of LOOP. You can read the ASFC documentation online at <http://loreandlutes.sourceforge.net>.

Example compilation:
g++ *.cpp -lasfc -lloop -lSDL -lSDL_image

The LOOP Object:

To use LOOP you really only need to take advantage of one class: LOOP_LOOP. If you want to program in your own commands for the scripting language you'll have to delve deeper into other classes but for now just know you need this class. The first thing you should do though is create a program that will run a generic loop script:

#include <loop/loop.h>
#include <loop/looplib_standard.h>

int main(int argc, char *argv[])
{
LOOP_LOOP LOOP;
LOOP_LOOPLib_Standard lib;
string file;
string class;
string member;

cout << "Linking the standard library to LOOP\n"; lib.Link(&LOOP);

cout << "What file would you like to execute? " cin >> file;
cout << "\nLoading: " << file << endl LOOP.ReadClass(file);

cout << "What class would you like to create an instance of?"; cin >> class;
cout << "\nCreating a new instance of class" oLOOP.NewInstance(0, class, SCOPE_LOCAL);

cout << "What member would you like to call: "; cin >> member;
oLOOP.GosubInstance(class, member);
cout << "\nGosubing " << member << endl;

cout << "Executing file for 100 iterations:\n" for(int i = 0; i < 100; i++)
oLOOP.RunInstance(0);

cout << "Finished\n";

return 0;
}

Go ahead and create a project, copy and paste that code, and compile. If everything works as it should then you can use this program to test LOOP scripts that show up in this document.

A bit about the source

Reading the source you'll probably notice the ReadClass() member is called. This member will read a text file and compile it in memory so that LOOP can execute it. The NewInstance command will create a new instance of class and the RunInstance will run an instance. Near the top you'll see an instance of LOOPLib_Standard created. This object is the standard LOOP library which includes basic commands for LOOP like #StrEqu and #Cout. The Link() member links this library to LOOP thus making all the commands in the lib accessible by LOOP.

Now test that code

If you compile and build the program above you should be able to run generic LOOP scripts (ones that only use the the standard LOOP lib). To test LOOP create the follow file save it as test.script:

@test

:test
#cout "Hello, World!"
#return
^

Next run the generic LOOP Scriipt runner and enter the following information:

Linking the standard library to LOOP
What file would you like to execute? test.script Loading: test.script
What class would you like to create an instance of? test Creating a new instance of class
What member would you like to call: test Gosubing test
Executing file for 100 iterations:

If everything works properley you should get these results:

Hello, World!

Writing loop script files

If you've programmed classes within the L&L editor before then you already know how to write LOOP scripts (if so you can just skim this section, be sure to read the Starting and Ending Classes section though as it differs from how you write L&L scripts in L&L). Otherwise I've got some explainin' to do. A LOOP script is simply a text file that includes a list of commands just like a normal program. Like any programming language LOOP requires that you write in a certain way (syntax).

Sample LOOP Script

@TestClass

:SayHello
#cout "Hello, World!"
#Return

^

LOOP is L + OOP

LOOP in some respects is an object oriented language, in that you write classes that contain members and you can have inheritance. It however isn't strongly typed, there are really only three variable types. So it's not truly an Object Oriented Language. Nevertheless, its important that you understand the basic concept of OOP before you try to program and use LOOP. Being that if you're reading this document you probably know how to program C++ I'll assume you know OOP.

Starting and Ending Class

Each script file you create will contain a class, the first line of all script files should include an at sign (@) and the name of the class you want this file to contain. For example if you want a class named TestClass the file should start out like this:

@TestClass

Class names can contain any character you want except [, ], (, or ).

To end a class place a caret at where the class ends. For example a generic class format is:

@ClassName
//Class Code
^

Anything after the carret will be ignored by the LOOP parser so you can place comments and notes there if you'd like. Be careful to end the class though as forgetting to do so can confuse LOOP and might freeze it.

Comments

LOOP uses C++ style comments, two slashes denote a line comment telling the LOOP parser to ignore everything until the end of a line. /* and */s are block comments and are ignored just as they are in C.

Commands

To actually make LOOP do something you have to use commands! Commands are programmed in C++ and this doc will explain how to make your own commands later on. To use a command type a pound sign (#) followed by the command's identifier, followed by any parameters you want to pass to it. Separate all parameters with spaces. For example the command cout takes a string parameter and will output this parameter onto the console:

#cout "This command couts"

There are three types of parameters LOOP accepts, ints, floats, and strings. Constants of those three types are passed just like that are in most languages. For example a string is passed by enclosing the string in double quotes ("), ints are passed by typing a whole number, and floats are passed by typing a number with a decimal point. Ints and floats may be positive or negative.

Example:
#cout "String "
#coutInt -1234
#cout " "
#coutFloat 1.234

Result:
String -1234 1.234

Variables are passed by enclosing the variable identifier in brackets [].

Variables

In LOOP there are three types of variables:

string Holds a string of characters like "Hello"

int      Holds an integer number like 123. Int is held in memory just like an
         int in C++.
float    Holds a number with a decimal point, like 123.4. In memory a float
         is actually stored as a double.

There are also two categories of variables public and private. By default whether a variable is public or private won't effect LOOP. You have to program in the difference yourself if you like.

To create a public variable write a percent sign (%) followed by the type of variable, a space, and in double quotes the identifier you want for this variable. Example:

%string "varString" //Creates a string called varString %int "varInt" //Creates an int called varInt %float "varFloat" //Creates a float called varFloat

To create a private variable write a dollar sign ($) followed by the type of variable, a space, and in double quotes the identifier you want for this variable. Example:

%string "varPubString" //Creates a public string called varPubString %int "varPubInt" //Creates a public int called varPubInt %float "varPubFloat" //Creates a public float called varPubFloat

To pass a variable to a command simply enclose the variable's identifier in []s. For example:

%string "message"
#StrEqu [message] "Hello, World!"
#cout [message]

Labels

Labels mark a location within a program. To create a label type a colon (:) followed by the identifier you'd like for this label. For example:

:label

Label's are used to mark the beginning of methods (sub routines), that #Gosub or #Goto commands call.

The Standard Library

In LOOP a library is a set of several commands. LOOP comes packaged with a sample library called the "LOOP Standard Library". Its not neccessary to use this library but its highly suggested. The commands in the standard standard library are listed below.

The rightmost column in the table below specifies what types parameters are passed. I for int, F for float, S for string. If a parameter is enclosed in []s that means a variable has to be passed. For example the table entry for RemoveSubStr looks like this:

RemoveSubStr Removes the sub string between the two passed ints. [S]II

So RemoveSubStr expects a string variable to be passed and two integer variables or constants.

Example:

@Test
%string "string"

:Test
#StrEqu [string] "String oops!"
#RemoveSubStr [string] 7 11
#cout [string]
#Return
^

Result:
String!


Command Desc Pass

Debugging

Cout             Writes a string onto the console                   S
CoutFloat        Writes a float onto the console                    F
CoutInt          Writes an int onto thte console                    I

CoutDebugInfo Display LOOP debugging information.

Conditional

IfStrEqu         If two strings equal execute the command on the    SS
                 next line. If the next command is #begin execute
                 all commands between #begin and #end. If the two
                 strings aren't equal skip the next line unless
                 it's a begin and which case skip to the #end
                 command associated with the #begin command.
IfIntEqu         Like IfStrEqu but compares two ints                II
IfIntGT          If int is greater than                             II
IfIntLT          If int is less than                                II
IfIntGTI         If int is greater than or equal to                 II
IfIntLTI         If int is less than or equal to                    II
IfFloatEqu       If float is equal to                               FF
IfFloatGT        If float is greater than                           FF
IfFloatLT        If float is less than                              FF
IfFloatGTI       If int is greater than or equal to                 FF
IfFloatLTI       If int is less than or equal to                    FF

Misc

Begin            Used to mark the beginning of a conditional
                 block for #If commands
End              Used to mark the ending of a conditional
                 block for #If commands

String Manipulation

StrEqu           Sets the the first string passed equal to the      [S]S
                 second
AppendStr        Add the 2nd string passed to the end of the 1st    [S]S
InsertStr        Insert 2nd string into 1st                         [S]SI
RemoveSubStr     Removes the sub string between the two passed ints [S]II
UpperString      Capitalize the 2nd string, place it in the 1st     [S]S
SubStr           Grab the sub string between the 2 int locals       [S]SII
StrSize          Return the size of a string                        [I]S

Int Manipulation

IntEqu           Set int equal to                                   [I]I
IntInc           Increment the value of an int by 1                 [I]
IntDec           De-increment the value of an int by 1              [I]
IntAdd           Find the sum of two ints                           [I]II
IntSub           Find the difference between two ints               [I]II
IntMul           Find the product between two ints                  [I]II
IntDiv           Return the ratio between two ints                  [I]II
IntMod           Find the modulo between two ints                   [I]II

Float Manipulation

FloatEqu         Set float equal to                                 [F]F
FloatInc         Increment the value of a float by 1                [F]
FloatDec         De-increment the value of a float by 1             [F]
FloatAdd         Find the sum between two floats                    [F]FF
FloatSub         Find the difference between two floats             [F]FF
FloatMul         Find the product between two floats                [F]FF
FloatDiv         Find the ratio between two floats                  [F]FF

Seek

Goto             Set the point of execution to a label             S
GoSub            Set the point of execution to a label. Return to  S
                 this point when the next #Return command is
                 called.
Send             Perform a goto command on another instance        SS
GoSend           Perform a gosub command on another instance       SS
Return           Used for marking the end of a method called by
                 a #GoSub or #GoSend statement

Sample LOOP script:

//This loop script will print all the even numbers between 0 and 100 //when the PrintEvens method is called.

@Mathematician
%int "counter"
%ing "mod"

:PrintEvens

#IntEqu [counter] 0

:Update

        #IfIntLTI [counter] 100
        #Begin
            #coutInt [counter]
            #cout " "
            #IntAdd [counter] [counter] 2
            #Goto "Update"
        #End

#Return

^

To run this script using the generic LOOP runner enter the following info: Linking the standard library to LOOP
What file would you like to execute? script.script Loading: script.script
What class would you like to create an instance of? Mathematician Creating a new instance of Mathematician What member would you like to call: PrintEvens Gosubing PrintEvens
Executing file for 100 iterations:

Creating Your Own LOOP Library

Once you've learned how to create your own scripts using the standard LOOP library you may like to create your own library of commands. Doing so is fairly easy once you get everything set up, but getting everything set up can be a pain!

The way LOOP handles commands is fairly easy, you create a function that maps to your command, then you tell LOOP where this function is, what command it links to, and what parameters are passed to the command. Whenever LOOP encounters your new command in a script it calls through a function pointer your function. This sounds easy but its not!

Anyways, the first thing you need to do is create .h and .cpp files for your library. Copy in the code for each file as seen in appendix A (near the bottom of the file).

You'll notice that the .h file includes the following headers: #include <loop/loopmacros.h>
#include <loop/info.h>
#include <loop/loop.h>

loopmacros includes macros that make it possible for you to pass a function within a class to LOOP, if you've ever used function pointers you may have encountered troubles with this before. The macro will create a static member in your library class that will map to the dynamic member to be executed. If you don't understand that don't worry just know you have to use the macros in there.

The info header includes an info object, an info object will be passed to the function you create for your new command. The info object holds information about what parameters are passed, what instance is passing it, and includes a few helpful members that make LOOP command programming easier for you.

And the loop header includes the main LOOP object which will get passed to a member your about to program called Link(). Link() will link() all of the commands in your library to the main LOOP_LOOP object.

Now go through the file and anywhere it says Insert<something>here insert the appropriate name. For example replace:

InsertNameHere: With the name of your library. For example MyLib. InsertCommandType: With a helpful description about what types of commands

you're defining below. InsertCommmandName: Insert the name of the command you want to create.

Pay close attention to this line:
LOOPPORT(LOOPCMDPRT_InsertCommmandName, LOOPLib_InsertNameHere, LOOPCMD_InsertCommandName);

LOOPPORT is the macro that defines members for your LOOP command that will be called whenever the command is encountered in a script. The first parameter passed will define a port function which helps resolve some issues with how function pointers work, the second param passed is the name of the class your member functions dwell under, and the third parameter tells the macro what you want your actual member function to be called.

If you want your library to include more than one command simply copy that line as many times as necessary replacing all the InsertNameHeres with the name of each command.

Next near the bottom of the .cpp file you should have these lines:

void LOOPLib_InsertNameHere::LOOPCMD_InsertCommandName(LOOP_Info oInfo) {
//Inset command code here
}

This is a template of what your member function will look like. Replace the Insert<something> text with the appropriate information. If you like you can change the identifier of the LOOP_Info variable (oInfo) passed.

Next scroll up to your Link() member, which will link your library to the LOOP object. Take a gander at this code:

poLOOP->NewCommand("InsertCommandName", &LOOPCMDPRT_InsertCommandName, this);

                poLOOP->AddParam(PARAM_INSERTPARAMTYPE1);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE2);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE3);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE4);

Replace all Insert<somethings> with the correct information. The AddParameter calls below tell LOOP what type of parameters will get passed to your new command in the order they should get passed. Possible params to pass include:

PARAM_INT           Pass an int variable or int constant
PARAM_FLOAT         Pass a float variable or float constant
PARAM_STRING        Pass a string varaible or string constant
PARAM_INT_REF       Pass an int variable
PARAM_FLOAT_REF     Pass a float variable

PARAM_STRING_REF Pass a string variable

You can add as many or as a few parameters as you'd like. If your library has more than one command (most likely) simply copy and paste the code for all the commands you need. For example a library for two commands called #WriteInt and #WriteString might have a Link() member that looks like this:

void LOOPLib_TestLib::Link(LOOP_LOOP* poLOOP) {

        poLOOP->NewCommand("WriteInt", &LOOPCMDPRT_WriteInt, this);
                poLOOP->AddParam(PARAM_INT);
        poLOOP->NewCommand("WriteString", &LOOPCMDPRT_WriteString, this);
                poLOOP->AddParam(PARAM_STRING);

}

The Info Object

As you write code for your command's function (replacing the //Inset command code here comment) you'll probably want to get information about the parameters that were passed to your command. You can get this information from the LOOP_Info variable. LOOP_Info includes the following members for reading parameters:
int    GrabInt       (int iNumber);
double GrabFloat     (int iNumber);

string GrabString (int iNumber);
int GrabIntRef (int iNumber);
int GrabFloatRef (int iNumber);
int GrabStringRef (int iNumber);

Set the iNumber param equal to which <int/float/string> is passed. For example if you had a command called drawline that took 4 ints for coods it might look like this:

void LOOPLib_MyLib::LOOPCMD_DrawLine(LOOP_Info oInfo) {

        FunctionElsewhereThatDrawsALine
        (
                oInfo.GrabInt(0), oInfo.GrabInt(1),
                oInfo.GrabInt(2), oInfo.GrabInt(4)
        );

}

Realize that if you have a member that gets passed variables both by reference and by non-reference the iNumber parameter for non reference grabbing members also maps to the reference passed variables. For example if you had a command called PassVars that took i[i]i and you executed this in a script:

#PassVars 10 20 30

Then:

oInfo.GrabInt(0) = 10
oInfo.GrabInt(1) = 20
oInfo.GrabInt(2) = 30
oInfo.GrabIntRef(0) = An integer representing the location of the intref

                      variable. This will be iLocation for the SetInt()
                      member if you choose to use it.

If you'd like to change the value of a variable passed by reference you can use the Variable Setting members of the LOOP_Info object:

void SetInt (int iLocation, int iTo); void SetFloat (int iLocation, double dTo); void SetString(int iLocation, string sTo);

Adding to this documentation

Currently there's a lot of information that could be added to this doc but hasn't been mostly because I'm busy coding :-). But if you have something that you think should be added, or if you use LOOP and know just what this doc needs feel free to help me. E-Mail me, Andy Stone, at astone42@attbi.com. Also if you notice any dumb spelling or grammar mistakes that just stand out and bug you e-mail me where the are and I'll try to fix those too. Thanks.

Errors, Bugs, Questions, Comments?


E-Mail me (Andy) or Eoin:
Andy Stone (astone42@attbi.com)

Appendix A: Template LOOP Library Class

templatelib.h

#include <loop/loopmacros.h>
#include <loop/info.h>
#include <loop/loop.h>

class LOOPLib_InsertNameHere

{       public:
        //- [Constuctors] -
                LOOP_LOOPLib_Standard();
                void Link(LOOP_LOOP* poLOOP);

        //- [InsertCommandType] -
                LOOPPORT(LOOPCMDPRT_InsertCommmandName, LOOPLib_InsertNameHere, LOOPCMD_InsertCommandName);

        private:
        //Vars

};

templatelib.cpp

#include "templatelib.h"

LOOPLib_InsertNameHere::LOOPLib_InsertNameHere() {
}

void LOOPLib_InsertNameHere::Link(LOOP_LOOP* poLOOP) {

        poLOOP->NewCommand("InsertCommandName", &LOOPCMDPRT_InsertCommandName, this);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE1);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE2);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE3);
                poLOOP->AddParam(PARAM_INSERTPARAMTYPE4);
        //Repeat for more commands...

}

void LOOPLib_InsertNameHere::LOOPCMD_InsertCommandName(LOOP_Info oInfo) {
//Inset command code here
}