Scripting system - The development of the Parser from hell

Published April 23, 2011
Advertisement
So I decided that I was going to start working on the scripting system for a FPS project my friend and I are working. I said hey, this should be relatively easy, at least until I started the project. . . Lets continue.

My thoughts that went into creating the scripting system.

Step1: Create a linked list class in which I could store all the script data
Step2: Write function to load in and store all script files in linked list
Step3: Iterate through the linked list and manage all the data that was acquired

This is what got me in trouble. Some questions arose while I was developing the linked list class.

How can i scan a directory and read in ALL script files in that directory?
In order to read in the files I need a text parser; not only to read scripts but to read in the script files, how do i do that?
What would happen if I changed my linked list class into a template class so I can use it anywhere I need to?

Anyways, this is what happened.

I scraped my list class and started using the standard C++ list class. This switch made writing code a lot faster because I no longer had to worry about adding functionality to my own class. I was merely writing my own class so that I could become my confident in my ability to use linked lists. I created a script template that my script files will follow. This was merely a step in creating something that I could work with once I started working on the parser. I decided to create a file called Script_Index.txt which would hold all of the filenames for the scripts that would be included in the engine. Yes, the filenames need to be added manually but this made my life easier and allowed me to stay away from the win32 API. Oh, did I mention that the Script_Index.txt file would support line and block comments because it will.

I then started writing the text parser.

Step1: Read in file
Step2: Parse file
Step3: store file
Step4: access file to begin loading all the scripts.

Okay, so step1 was simple enough. I created a fstream member variable called tempFile to open the required Script_Index.txt file in a stream. This allowed me to access the text contained in Script_Index.txt. Moving on to step2 was the hard part. I began by reading in the while file by iterating though Script_index.txt until the eofbit was reached. This caused multiple problems.

prob1: stored all carriage returns (\n) and white spaces.
prob2: All of the characters were appended to a string and thus, there was no formating and for the string to be parsed, i would have to scan through the WHOLE string
prob3: This method was very inefficient and I just simply did not want to do this.

So I created a solution.

Why not use the getline() function on tempFile to read in a line, then parse through the the string which would be stored in a buffer and all needed information would be taken out after the parsing was complete. This is exactly what I did.

My code is still a little broken but I will gladly share it with the world so that others can learn from what I had to go through.

bool ScriptMgr::ImportScripts()
{
// Variables
ifstream tempFile;
string buffer;
int count = 0;

StringList::iterator listIter;

tempFile.open("scripts/Script_Index.txt"); // Open the file Script_Index.txt
// This file contains all the filenames for the scripts located
// In the script folder. These filenames need to be added manually.

if(tempFile == NULL)
{
cout<<"Cannot find file Script_Index.txt"< return false;
}

assert(tempFile.is_open());

while(!tempFile.eof())
{
// Scan through Scripts_Index.txt until the end of file
// This stores all the characters from Scripts_Index.txt into a buffer.
getline(tempFile, buffer);

if (buffer == "")
{
// continue to next line
continue;
}
else
{
// parse the line
for(int i = 0; i <= buffer.length() - 1; i++)
{
if(buffer.at(i) == '/' && buffer.at(i+1) == '/')
{
// we have reached a line comment, skip to next line
buffer.clear();
break;
}

else if ((buffer.at(i) == ' ') || (buffer.at(i) == '\n'))
{
// add the filename stored in buffer to the list
cout< myScripts.push_back(buffer.substr(0, i));
}
}
}
}
/*
for (listIter = myScripts.begin(); listIter != myScripts.end(); listIter++)
{
string currentString = *listIter;
// print contents of list
cout<<*listIter->data();
}
*/
return true;
}



I want to leave this code with a notice, i have not figured out why the code inside the last else if statement is never run, and if you do decide to use this code in your own projects that you ask for my permission first. I will most likely say yes, but it is nice to know that someone else views your work as helpful.

Anyways, after writing the code, I realized that I do not really need a linked list containing the filenames of the script files, I can just use an array of strings to store all the filenames. During the initial loading of Script_Index.txt, i can iterate through the file line by line, ignoring line comments and box comments to count how many script files I need to store. I can then create an array of strings with the necessary amount of space needed to hold the filenames. This is also wonderful because after I read in Script_Index.txt and load in all the script files, I can extract the data needed, store it in the classes that require the data and then just get rid of the list all together. If you are wondering why I would do that, This is because the script files that I am loading in are not used during runtime, only loadtime when the engine/game loads at the beginning. The script files only contain information about a corresponding weapon and or a Main menu element for example.

More will be added soon; most likely after I fix my code and complete my simple text parser.

- BIASxENVY

As requested, the file that I am parsing at the moment looks like the following snippet. I have considered using Angel script and Lua but I decided to implement my own script. I figured this would allow myself to strengthen my use of C++.




/*
Written by BIASxENVy
Script_Index.txt stores all of the filenames for the scripts.
ProjectFPS
*/
// System

// Weapons

Desert_Eagle.Weapon
Assault_Rifle.Weapon

// GUI
Main_Menu.GUI

// Players


0 likes 2 comments

Comments

Michael Tanczos
You should provide a sample script you are parsing. Also, look into Lua.. it's extremely easy to integrate unless you are just out for the challenge of creating your own script language.
April 23, 2011 11:23 AM
BIASxEnvy
I added the script to the post if you are interested in looking at what I am working with. As you will probably see I do not have block comments in he code yet. I will be adding the block comment support within the next couple of days.
April 24, 2011 04:50 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement