Sunday, April 17, 2005

The Wonderful World of Grammars: Randomly Generated Content for Roleplaying Games using Grammars

Image Hosted by ImageShack.us

Do you know what I think are really cool? Grammars.

I like the idea of randomly generated content. I like it a lot. I also like Grammars because they're pretty simple. Your programming language of choice was defined using a grammar. There are many different types of Grammars and many have scary looking definitions with lots of nasty set theory stuff. Let's not get bogged down in that, rather let's look how a Grammar might be useful for a computer game, especially a role playing game.

To sum up: Grammars are cool.

They've been used to model the growth of molds and cities, even music and cool stuff like that. You could use them to write nonsense books, make up words, zen koans etc.

Issues

The problem is grammars are to some extent limited. You CANNOT translate one (human) language into another using a grammar. CANNOT. Language is too fluid grammars are far too stiff. Also the very nature of language means there may be extra detail contained in a sentence that cannot be filled in by a language that does not record that detail. But I'm getting off track. The important question is - are grammars too limited to do really groovy stuff with games?

With that question in mind let's set out to discover the wonderful world of grammars. Now, I wanted to be able to try out a few different grammar definitions, to see what was what in the world of grammars. This isn't the kind of thing I can program in the middle of my already cluttered computer game! Instead it's the perfect thing for a small seperate application. It will be cool and shiny and allow us to experiment with any kind of grammar we wish. Does it sound groovy? It is!

Formal Grammar Definitions

Did the word formal make you cringe. I'll look through the most basic grammar definition explainng what it's actually saying in plain English. Hopefully this will fire you're imagination a bit.

Let's look at a Context Free Grammar. Context-free means it the context of something doesn't matter for the rules to go ahead. That's sounds a little confusing so let's clear things up with an example.

Imagine infront of you is a white table.

Image Hosted by ImageShack.us

Now on this table are some smarties.

Image Hosted by ImageShack.us

The smarties are arranged in two rows. The first row of smarties, about five smarties long, are all blue.

Image Hosted by ImageShack.us

The second row of smarties also about five smarties long are all red - apart from the third smarty he's blue - and looks very out of place, maybe he feels a bit initimated too.

Image Hosted by ImageShack.us

Now imagine a non-context free grammar. That means context could come into play! So let's suppose we have a non-context free grammar rule - if could say if there's a blue smarty surrounded by two reds then change it to a green smarty.

Image Hosted by ImageShack.us
That's what context is. In a context-free grammar it would be hard to put this type of rule in. But to define context free grammars are simpler. What we'll do next is look at the formal definition of context free grammar.

Formal Definition Of A Context Free Grammar

A context free grammar is a four tuple (a pair can be thought to be a two-tuple) defined:

(V, E, S, P)

V is a finite alphabet of non-terminal symbols.
E is a finite alphabet of terminal symbols.
V intersect E = the empty set
S is an element of V
P is a set of ordered pairs {alpha, beta} called production rules
That's the main part of the definition. Writing the mathmatical notation is a little tricky. I'm might try and find something a little more elegant. Let's look at the formal definition for a production rule and then we'll go through and explain it in English.

alpha and beta are elements of (V union E)* and alpha contains at least one symbol from V
You would be surprised how straight forward this all is if you know the lingo. We don't really need to know if for the game though, maybe you'll be able to pick some of it up from the following explanation.

Let's explain this line by line.

1. V is a finite alphabet of non-terminal symbols.

V is a set. Set are like boxes that are INSIDE your head - obviously not physically. They can be infinite (the set of all numbers 1,2,3, ... there's no end that's means it's infinite - yeah?) or finite the set of fingers of my left hand (five - isn't strange that we always include the thumb in the set of fingers but would never other wise call it a finger). So we had a finite set this means we can count the elements. It can still be very very very large but not infinite.

It's an alphabet of symbols. I guess alphabet means the symbols are all different and have some meaning. Let's say our V is the following.

V = {[a man's name], [the number of people in a city], [a series of arm movements], [the position of a space station]}


Looking at that you might notice something ... they're all descriptions rather than actual data. Interesting no? Well this is to do with non-terminal. For instance a finished sentence might.

Roger Redhat likes Jenifer Yellowhat.

This is a fine sentence each word here can be thought to be terminal. The grammar can be thought to have finished. Non-terminals are what the grammar uses as it approaches being finished for instance the above sentence may have gone through the following stages. (I'm using [,]'s to mark non-terminals).

[sentence]
[personage] likes [thing]
[young boy] likes [personage]
Roger Redhat likes [young girl]
Roger Redhat likes Jenifer Yellowhat
I hope you can see how that might have worked. Non-terminals means the grammar shouldn't terminate yet.

2. E is a finite alphabet of terminal symbols.

The actual symbol here isn't a E it should be an epsilon (I think that's the right word). Looks awfully similar to the previous rule and it is - exception being that this is the set of terminals - that means rules that declare an end. They cannot be modified anymore.

E = {Peter, 7 million, ((0,0,0),(17.4,12,8.8),(45.2,6,8.9)), (X:2.111, Y:45.7A Z:99.3)}

And that kind of thing. Generally you don't fill these sets with random crap like I have instead you craft them lovingly so they'll be able to create cool things. You want cool things don't you?

3. V intersect E = the empty set

This means anything that's a non-terminal can't also be a terminal because it just wouldn't work. And anything that's a terminal can't also be a non-terminal. Pretty common sensical :)

4. S is an element of V

S here stands for the start symbol. It has to be a member of V - the non-terminals or there's no point have a grammar as it would already be finished. Examples of start symbols might be:

[A made up book]
[A interesting world]
[A dungeon]
[A galaxy]
[A space ship weapon]
[An arch-nemisis]

Then from this we keep going applying rules until we have a big group of non-terminals and whatever we wanted to make has been made.

P is a set of ordered pairs {alpha,beta} called production rules. Alpha and beta are elements of (V union E)* and alpha contains at least one symbol from V

The first thing the gets my attention is that (V union E)*. It's a bit odd. What it means is any possible combination of terminals and non-terminals. These are the rules basically saying "If you see this stuff" then "make it look like this stuff". It's basically the production rules. Applying a production rules is often called a derivation. Let's have a look at an example of some rules.

[CATS] -> [CAT] [CATS]
[CATS] -> [CAT]
[CAT] -> "Pete the Cat"
[CAT] -> "Jim the Cat"
[CAT] -> "Growly the Cat"
[CAT] -> "Ginger the Cat"
[CAT] -> "Tooth the Cat"
[CAT] -> "Furball the Cat"

Well these rules could produce many derivations if we assume S, the start symbol, is [CATS]. The final result could be "Jim the Cat", "Jim the Cat Jim the Cat" or "Furbal l the Cat, Growly the Cat, Pete the Cat" any possible combination of cats.

Looking closely at that last rule it seems a bit off. It seems we could get round the non-context rule. So we'll just self impose that :D On to the code now.

The Program: Grammatatron.

That's not actually my projects name but you may wish to consider it. It does sound pretty cool. Let's give the basic run down of what I want in this project.

A window displaying the output.

For now this is text only output (we're not going to have graphical interactive worlds). The non-terminals will be strings of text. This is just for ease of use and simplicity at the moment. They could also be instructions, functions, rules, images anything you could imagine sticking at the end of your grammar.

Example output might be:

"You are on the contient of Zaabaa. It's famous exports are bannanas, mangoes, and wool. Currently you're in a cave."


And hopefully we'll be able to do things a little more fancy that that. A lot of it is up to how you write your grammar rules. The whole point of this is allow us to experiment in this fashion and see how much rich, luscious detail we can create.

A Text Box to Write The Grammar In

Here we'll have all our grammar rules. We'll also want easy saving and loading functions. Possibly we could add some highlighting. Maybe even auto-complete of known non-terminals. Pretty fancy pretty swish. Of course that means we'd have to have set definitions of what a non-terminal looked like. A restriction that's probably worth imposing.

A big red GO! Button

Note, finally button may not be big, or red. This button would say "given these grammar rules" go generate us a gramar or tell us where the grammar has gone wrong.

A LUA Grammar Interpreter

Yeah, this ones a bit of a surprise huh? We're going to have another text box. This one is going to take LUA code. When run LUA is going to read our grammar the follow our grammar. We can edit the way it does this on the go - impressive heh? This would also benefit from highlighting and a nice debugger to. The intructions are going to pretty simple so we could potentially write the code in a more fully fledged GUI.

The best thing about this is we could have a number of LuA files each representing a slightly different grammar. This allows us to experiment.

Creating the Program

Boot up Visual Studio. We're programming in C# as it's the best programming language in the world :D Then start a new project and select a windows form project.

Image Hosted by ImageShack.us

Now we have a nice new empty project. Well apart from the form crap that's in there but not to worry about that for now.

In the toolbox grab the tabControl. Drag this over into your form. Now in the tabControl go to properties. Go to Dock and choose fill. In my version of the IDE when I click on Dock it brings up a small gui with five buttons, click the central most one, that's fill. The tab control should not have expanded to fill the entire form.

Image Hosted by ImageShack.us

Grabbing a Tab Control.

Image Hosted by ImageShack.us


Go back to properties and this time choose TabPages. There will be a button with three dots when you click on it. Press the button with three dots. Add two tab pages, this can be done by clicking the Add button twice. Now go the tab pages properties and give both tabs a name. I named the first Grammar Editor and the second LUA Interpreter. After doing this, close that window.

Image Hosted by ImageShack.us

Press that blue add tab hotlink twice.

Image Hosted by ImageShack.us

Change the tab names!

In the form designer you'll now see two tabs! And you can click on them both. Each gives a different tab page which you can fill with handy controls. Let's start with the second tabPage the one I named "LUA Interpreter".

LUA Interpreter

We're going to keep this pretty simple. Add a TextBox from the control panel. Go to Dock choose fill again. Then set MultiLine to true, now it will fill the entire window. This is where our LUA code is going to go. We may want to change the font later too, as the current one sucks.

Image Hosted by ImageShack.us

Grammar Editor

This tab will be slightly more complicated. I threw on two text boxes each taking up roughly half the form. The one of top we're going to use a output. Therefore we don't want our user typing in it. (Even if the user is only ourselves it looks prettier this way). So we set ReadOnly to true. I also renamed it so it was called textBoxOutput. I don't usually rename stuff when I'm using the form wizard but then I usually create very small programs.

The other box I called textBoxGrammar and I set it's multiline to true too. We don't set the readonly flag because this is where we'll be typing in our grammar. Over each box, I added a nice little label too so they're easily identifiable.


The next thing to work on is loading and saving for the Grammar. We want to save our grammars, the ones that are particularly nice we can cherish and leave as a legacy in our wills and so forth. Therefore I added two buttons and dropped them quite near the grammar window.

Image Hosted by ImageShack.us

One button would be used to save, called buttonSaveGrammar the other used to load called buttonLoadGrammar. Then to make load and saving more user friendly I went back to the tool box and drop a loadFileDialog and a saveFileDialog onto my form. These appear at the bottom of the designer and we don't really have to touch them for now. Though I renamed them so "loadGrammarDialog" and "saveGrammarDialog".

We've done enough as we can in pleasent form designer now we actually have to write some code!

Load and Saving Grammars

We want stress free loading and saving of these grammars. I also wanted to make it fairly idiot proof as I often save over stuff.

Let's do saving first. We're going to save in our propiertary format :D Only because this is even simplier than saving it as a text file. First we need to add some using statements at the top of the code file.


using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

I love serialization it's great. If you ever can use, use it. With a computer game though you should be careful with it, make sure you're not saving lots of redundant stuff. Here we're using it very simply. In the form designer double click on the save button. This will hook up and event and take you it.

private void buttonSaveGrammar_Click(object sender, System.EventArgs e)
{
}

Save in the most general case is going to save to the last saved position. If there is no last save postion then we give the user a save dialog and ask her where he'd like to save. We check the last save position by looking at where the openGrammar dialog last opened a file from.

If it hasn't opened a file yet, then we can assume this is a new grammar being written and we prompt the user to save it where she or he would like. We're going to have a look at the code now so get ready!

if(openGrammarDialog.FileName == "")
{
//Didn't load a file, instead wrote
//from scratch
if(saveGrammarDialog.ShowDialog() == DialogResult.OK)
{
WriteGrammar(saveGrammarDialog.FileName);
}

Saved = true;
GrammarEdited = false;
}
else
{
WriteGrammar(openGrammarDialog.FileName);
}


Seem's pretty straight forward. The first line that may cause worries is:

if(saveGrammarDialog.ShowDialog() == DialogResult.OK


This is saying, load the SaveGrammarDialog - so the user is presented with a window where they can choose to save their file. If they have second thoughts and cancel then the if-statement fails and the function is returns. If on the other hand the user says "ok save here" then the saveGrammarDialog records the filename to save to. We then pass the filename along to our SaveGrammar function, that we haven't written yet.

Dialog Results is an enumeration of all the common dialog buttons, so Yes, No, OK, CANCEL etc. When the dialog closes it returns the button it was closed with. Was it closed by the user pressing OK or was it closed by the user pressing cancel?

The next bit that might be confusing is


grammarSaved = true;
grammarEdited = false;

Don't worry about them for now. Just define some globals and carry on.


bool grammarEdited = false;
bool grammarSaved = false;

That's pretty much the pressing the save button function. The actually writing is squirrelled away and now is the time to unsquirrel it. This function is so simple and groovy we're going to look at it all in one go:


private void WriteGrammar(string fileName)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, textBoxGrammar.Text);
stream.Close();

textBoxOutput.Text += "\nFile Saved " + fileName;
}

First line says we're going to be writing out binary (rather than say XML which we could do if we took a fancy to it). Next we create the file stream using the file name. If it's present on the hard drive we'll open it and write to it, if it's not there then we'll create it. We're only going to be writing. FileShare doesn't concern us. Then we use the formatter to seralize our text. It handles all the writing to a file for us. After this close the stream and report a succesful save. Couldn't be simpler!

Now to loading!

We can set up a "pressed-the-load-button-event" by going to the form designer and double clicking on the load button.

private void buttonLoadGrammar_Click(object sender, System.EventArgs e)
{
}


We'll get something like the above.

Now in here we're going to add a bit of code. The first code we're adding is going to be checking for possible mistakes by the user. If we're potentially overwriting something the user might wish to keep we're going to warn him.

How do we know what the user might want to keep?

*Has she saved it?
*Has he edited it since it was loaded?
*Is there anything to save?

So if what's-currently-in-the-grammar-window is not nothing and has been edited and hasn't been saved then we should prompt the user. So how do we do this? We'll be making use of the two bools we introduced in the Saving a file section.

bool grammarEdited = false;
bool grammarSaved = false;

GrammarEdited means "Have we just edited what's in the window?". And saved means "Have we just saved what's in the window?". Pretty straight forward, no? Checking if the Grammar Text Box is empty is a simple comparison of the text. Here's the conditional:


if(!(textBoxGrammar.Text.Length == 0) &&
grammarEdited &&
!grammarSaved)
{

If we pass that if-statement then we warn the user. Now if it's the case that we're potentially losing important data, we're going to ask if the user's sure. There's lots of built in stuff that makes this really easy. We're going to build a YesNo box, and ask "Are you sure you want to save?", if the user says yes we save. If he says no we cancel. Here's the code:


if(!(textBoxGrammar.Text.Length == 0) &&
grammarEdited &&
!grammarSaved)
{
//Should have a message box asking
//if want to overwrite stuff in box
//if it's not saved
if(MessageBox.Show ("Load without saving?", "Really?",
MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk)
==
DialogResult.No)
return;
}


If the user say's "No, don't load without saving my data first" then we drop him back to the window to save (just do a void return from this statement). If he says "Yes" load without saving then we carry on.

So let's assume he carries on. We do a piece of code very similar to the save dialog but this time we're using load:


if(openGrammarDialog.ShowDialog() == DialogResult.OK)
{
ReadGrammar(openGrammarDialog.FileName);
}

grammarEdited = false; //we couldn't haven't edited the data
}

Once again though we have a function that requires unsquirreling. It too is beautifully simple.

private void ReadGrammar(string fileName)
{
try
{
textBoxGrammar.Clear();
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
textBoxGrammar.Text = (string) formatter.Deserialize(stream);
stream.Close();

}
catch (Exception e)
{
// Let the user know what went wrong.
MessageBox.Show(this, "The file could not be read.",e.Message);
}

}

I put try and catch statements here too! So we can now load and save but there's still a little thing missing. How do we know when the textBoxGrammar has been edited? Well there's a nice delegate for this that we can hook an event up to.

The easiest way to do this is to go to the form designer. Select textBoxGrammar. Then go over to the properties and select the lightening bolt icon. Scroll down to TextChanged then double click in the blank box next to it and this will hook and event up there for us.

private void textBoxGrammar_TextChanged(object sender, System.EventArgs e)
{
}


You should get some thing like the above. In here we're going to play with our bools. (HAHAHA! I'm a comedy genious)


//If we edit the text then we changed the grammar
//we record this in a bool so we can do a save prompt in necessary
private void textBoxGrammar_TextChanged(object sender, System.EventArgs e)
{
grammarEdited = true;
grammarSaved = false; //We've no longer have saved
//what's in the box
}


This is getting pretty fancy now so let's put a last shine of polish on it. Go back to the form designer and select the "openGrammarDialog" go to it properties (we're back to properties now, not events). I've decided Grammar files will be called .grm's.

So we'll set DefaultExt to "grm". Then we scroll down to Filter here we input "Grammar Files | *.grm". This means when opening files tell the user we're opening grammars files and look only for files with the extension .grm. Now goto the saveGrammarDialog and chose it's properties and make the same changes to the same fields.

That's pretty fool proof loading and saving. Feel free to load and save some files and marvel at how it doesn't break (hopefully) :D.

The GO! Button

Everybody needs a big red go button. Though as I said it's not going to be big or red rather it will be small and gray. Simply grab a button from the tool box and drop it on to your form.

Upon pressing the GO! button we're going to have LUA run grammar and then execute a run on it. Every time we press the go button LUA will reread the grammar and do a new run upon it. Not terribly efficent but it's not like this program is mission critical. Before going any farther I think you should read the LUA tutorial.

Okay now set up LUA so it's referenced and the .dlls are all in the right places. If you can't remember how check out the LUA tutorial.


First let's add the using statement.

using LuaInterface;

Right now let's add a nice LUA global.

Lua lua = new Lua();

Everything else takes place within the GO! buttons OnPressedEvent. So go back to the form designer and double click on the button to automatically hook up this event for you. Here it is all with some code added.

private void buttonGo_Click(object sender, System.EventArgs e)
{
lua["GRAMMAR"] = textBoxGrammar.Text;
lua.DoString(this.textBoxLua.Text);
}

We're saying make the GRAMMAR variable in lua store all our grammar text. Then run our grammar processing code. Pretty sweet. We're almost ready to go! All that's left to do is have LUA output something to our program. To do this we'll need to write an output function. I'm going to preface the functions I'm sending to lua with the string "lua". Currently I'm undecided how much preprocessing the Grammar Text will go through before we pass it on to Lua to deal with - for now it's going to be none. Let's look at the output function we intend to hook up to Lua.

public void luaWriteToOutput(string output)
{
textBoxOutput.Text = output;
}


Simple. Now we just want to have Lua be able to use it. This is no big problem and we'll take care of it in the forms constructor.


public Form1()
{
InitializeComponent();

//Do Lua stuff
lua.RegisterFunction("Output" , this, this.GetType().GetMethod("luaWriteToOutput"));
}

Before we test this. Let's make coding a little easier on the eyes and change the default font in the code windows - both the grammar one and the lua one. Select the text box go to font and choose a nice font. I chose Lucida Sans Console. Wild speculation here but I believe the word console means it was specially designed for the computer screen and columns will line up much better. So that's the one I chose and set for both text boxes (the grammar one and the lua one) - now on to the test.

Boot up your trusty GRAMMARATRON program. Select the second tab and type:


Output("Crazy Horses! Waaaaaaaaaaaaaaaaah");

Sit back and admire the font for a bit. Now go back to the first tab and press "THE BIG RED GO BUTTON THAT IS NEITHER BIG NOR RED". And you should see some text go to the output window. Isn't that great!

I think this is going to be a two parter. So to close this part we're going to add a main-menu for loading and saving of our LUA files. Then in the next part we'll build our first fully functional LUa grammar parser / generator - won't that be fun! Then we'll all be free to experiment with grammars and hopefully our games will grow more diverse because of it!

For instance imagine book that generates each page as you read it, the generation function based off the page numbers as well as other facts about the book - no of pages, colour, binding etc all of which were also randomly generated. Everything becomes generated from everything else. This is how, in part, games like elite worked.


Adding a menu and loading saving for our lua files

So on the first tab we've gone with buttons, which while possibly a bit non-standard at least they're in esay reach. The second tab though we'll be doing programming I don't really want to take up anymore space. Therefore we'll add a menu bar with a file menu and to options "load lua file, save lua file" we can also add more stuff a we go.

This is all pretty easy and can be done in the form designer. We take the mouse on over to the tool box and choose MainMenu. Then we draw the main menu so it's at the top of our form.


Image Hosted by ImageShack.us


This produces a little white box with the caption "type here". This is where we can add our information. It's quite common to have the first menu item be File then Edit then ... and finally Help. It's very good to stick to standards like these it makes that program instantly useable by anyone who comes along. But I'm going to break with tradition - I'm going to have my first menu as Lua. For now the only menu item but it's easy to imagine adding one that would say grammar.

Clicking on the "type here" text allows you to type out your menu. I typed in "Lua" then under that menu I added Save Lua File and Load Lua File. I want to put in the same idiot proofing as before that will stop me saving over work or losing things that could be avoided!


Image Hosted by ImageShack.us


As we're loading and saving let's add two more dialogs. An openFileDialog and a saveFileDialog. Let's call them openLuaDialog and closeLuaDialog instead. Now we can use these for easy loading and saving of Lua files. Great! Let's get to the code. Like before we want to program an event handler that will handle the event of "Load Lua File" being selected from the menu for instance. The easiest way to get to this is to double click the relevant menu choice.


//Load A Lua File
private void menuItem2_Click(object sender, System.EventArgs e)
{

}
Of course you can (and probably should) rename this in the properties window of the form designer. That way you won't be dealing with the rather ambigous "menuItem2_Click".

We don't want to jump straight into loading a file though. First we want two global variables that will help us idiot proof our editing. So let's add those.

private bool luaSaved = false; //has the Lua code just been saved?
private bool luaEdited = false; //has the Lua code just been edited?

Now we're a little bit more prepared. Let's write our load function. We can pretty much copy the start from the buttonLoadGrammar_Click function. All we need do is change some of the variable names.

I've given my text box on the second tab the name "textBoxLua". So the condition for warning the user that he might be losing data goes as follows:


if(!(textBoxLua.Text.Length == 0) &&
luaEdited &&
!luaSaved)


and underneath this we can have the exact same code as in the Grammar loading function.


{
//Should have a message box asking
//if want to overwrite stuff in box
//if it's not saved
if(MessageBox.Show ("Load without saving?", "Really?",
MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk)
==
DialogResult.No)
return;
}


the end of the function is also very similar.


if(openLuaDialog.ShowDialog() == DialogResult.OK)
{
ReadLuaFile(openLuaDialog.FileName);
}

luaEdited = false;
}

We need to create a temporary loading class to allow the code to compile.

public void ReadLuaFile(string fileName)
{
}


Before going any further let's fix up the luaEdited varaible so it's actually useful. In the form designer go to the second tab select the lua text box and go to the event properties. Should be symbolized by a lightening bolt and be above the properties window. Then double click the white box next the Text Changed event. That should produce the following:


private void textBoxLua_TextChanged(object sender, System.EventArgs e)
{

}


And inside we put the file


luaEdited = true;

Reading Serialized Lua Files

Plain text reading can come later for now we'll just do the serialized versions so we have something working. This code is very simple and based off the Grammar saving code.

I'll think we'll use the extension .gin for grammar interpreter files. We can use the extension to determine how we will go about storing the file - serialization or plain text.


public void ReadLuaFile(string fileName)
{
if(fileName.EndsWith(".gin"))
{
try
{
textBoxLua.Clear();
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(fileName, FileMode.Open,
FileAccess.Read, FileShare.Read);
textBoxLua.Text = (string) formatter.Deserialize(stream);
stream.Close();

}
catch (Exception e)
{
// Let the user know what went wrong.
MessageBox.Show(this, "The file could not be read.",e.Message);
}
}
else // save as plain text
{
//the user might want to call it .txt or .lua or whatever
//no restrictions will be placed on the extension but only plain
//text will be written.

/** We'll write this later **/
}
}


That's all pretty straight forward, we can finish up by fiddling with the properties of the openLuaDialog. We'll set the default extension to "gin". And in filters add the following string "Grammar Interpreters | *.gin|Text File|*.txt|Any File|*.*". This leaves it quite open for the user. Of course at the moment the only format supported is .gin, because it's the easiest to program. That's loading done - now to saving.

Saving Lua Grammar Interpreters

We can hook up a Save Lua event much in the same way we hooked up a Load Lua event. Just double click on the menu item and allow visual studio to do all the hard work for you! Which should get you to the following:


private void menuItem3_Click(object sender, System.EventArgs e)
{

}



Once again it maybe advisable to edit menuItems3 name so it's a little more clear "LoadLuaFile" for instance. The code is going to be very similar to the SaveGrammar function so we can copy, paste and edit. Quickly we'll produce the following function:


private void menuItem3_Click(object sender, System.EventArgs e)
{
if(openLuaDialog.FileName == "")
{
//Didn't load a file, instead wrote
//from scratch
if(saveLuaDialog.ShowDialog() == DialogResult.OK)
{
WriteLua(saveLuaDialog.FileName);
}

luaSaved = true;
luaEdited = false;
}
else
{
WriteLua(openLuaDialog.FileName);
}
}


Pretty simple of course we need a skeleton function to allow this to compile - easily done


private void WriteLua(string fileName)
{
}


We can fill the above in pretty easily as it's like a minorly extended version of the saveGrammar function.


private void WriteLua(string fileName)
{
if(fileName.EndsWith(".gin"))
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, textBoxLua.Text);
stream.Close();
}
else //Save as plain text
{
/** Code needs to be written **/
}

textBoxOutput.Text += "\nFile Saved " + fileName;
}


Finally a bit of polishing. We go back to the form editor and goto the properties of saveLuaDialog. Here we make the same changes as we did to openLuaDialog, namely we change the default extension to ".gin" and add the following line to filers: "Grammar Interpreters | *.gin|Text File|*.txt|Any File|*.*".

To test this write a simple text program. Such as "output("hello world");" then save this lua file, close the program, run the program, open the saved file and run it. In the output box it should say "Hello World".

Plain text loading and saving is left as an excerise for the reader, as is the command Lua>Save As ... which might be useful. You may also wish to add "New Lua file" and maybe a Grammar section and File section to the the Menu Bar. (I've added a file option to mine, it has a "File> Save All". To add keyboard-shortcuts you add the character '&' before one of the letters in your Menu Item's Name, this will then be the shortcut-key for it. For instance I use 'Alt-F then S' all the time for saving. I've mimicked that here with "&Save All" under the file menu. So I press Alt-F this gets me into the file menu, pressing S runs the Save All event handler.

In the next part we'll be interpreting the Grammar. To get you started you may wish to try something like this in lua interpreter:


Output(GRAMMAR);

Now everything you type into the Grammar box will be faithfully reproduced in the output window - mess around with this and have fun.

Download icon.[Grammatron Code Version 1]
Post a Comment