dfn parameter concept

Want to discuss changes to the UOX3 source code? Got a code-snippet you'd like to post? Anything related to coding/programming goes here!
Post Reply
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

dfn parameter concept

Post by punt »

It is pretty well known, I don't like the dfn syntax, as it isn't self defining nor consistent. It has different behavior based on the "meaning" of the file (intended to be used), so one can not write a general parser, and of course, one has to know this "intention" via some means (it isnt defined some way in the dfn data).

Anyway, as I moved over to NSPR, without a lot of change, I have a general parser. One thing it does, for potnetial speed differences are:

Each "section" has the following:
title (what is in the [])
Then for every non comment line, the data is stored as :

Code: Select all

struct section_data
{
     UString key ;
     std::deque<UString> params ;
}

And the secition lines are stored in a std::deque<section_data>
This allows all parameter parsing to be done on load, versus run time. Of course, that means a standard way to delimite parameters needs to be done (one can't use space, for that works if one knows somehow that the tag currently be worked on has a multiline value). For know, I have used ";;" (but it could be anything that is unique).

Once one has a Section defined (with the above type of internal structures), then a generic Section Store can be written, that can parse a file, or direcotry of files, and store it (or fire virutal methods to do so).

Now, what does this mean to UOX3 if they wanted to do this? Only one change, standardize multivalue tag entries to have some unique deliminator, and it works.

I would caution the other potential chnge, is there are places where : [ITEM ###] exist. That, should probably be revisited as well. (place the ITEM as a tag intenal? or some othe standard way? I have suggestions, but of course, they are probably too emotionally attached for all to pursue.

At any rate, I have the parser working wiht NSPR for the above described change.
giwo
Developer
Posts: 1780
Joined: Fri Jun 18, 2004 4:17 pm
Location: California
Has thanked: 0
Been thanked: 0

Post by giwo »

I recall in the past you wanted to do much the same thing...

In that scenario, I believe one of the goals was to return to a format more like
[SECTION ITEM ####]
{
}

vs the current
[####]
{
}

would that still be required?

As for a standardized delimiter, while the space has proved effective in the past, I have no problem with finding a good unique separator to put in between values. The only major issue is forcing all the scripts to be updated once again.

As for things like [ITEM ####] I'm suprised that script even still functions, as I'm pretty sure we lookup our item entries to be an exact match to whatever is in between those brackets.
Scott
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

giwo wrote:I recall in the past you wanted to do much the same thing...
yes, the fundemental issue hasn't gone away.

Lets examine the objective, not the implementation.

1. Data definitions/formats should be self defining
2. Data syntax should be consistent, and parsable without knowledge of the data.

What those say, in a nutshell, is :

1. I should not have to know the file name, or some outside source of information, to understand what type of data I am dealing with. It should be identified in the data.
2. I should not have to be aware of the data content, to be able to parse it (like I can parse any xml file, without understanding the data within it.


Ok, now apply those two objectives to the DFNs. Clearly, since some times you have tags like : "tag = value " , "tag = value value", "tag = value,value" , and "tag = value with a space" , one can not parse the data without knowing something about it. so it fails item 2 at least.

Now, think about the different data files scatter around. Unless you know the directory, and sometimes the file name, you don't know what type of data the sections are describing. It would appear an attempt was made to get around that, with items like : [MENU ###], [ITEM ###], and [CREATURES ###} scattered in some of the files, mostly menu related ones. So it doesn't meet critiera #1 either.

Those two points are my fundemental issue with the format. I have other "issues" with it, but they are not nearly to the scale as these two. Imagin if you had to have a custom xml parser (just a parser mind you), to be able to parse any XML file, and the location of that file (or name) had some bearing on how you parse it. It would become a maintenance nightmare.


So I am open to any modifiations that tackle these two issues.
In that scenario, I believe one of the goals was to return to a format more like
[SECTION ITEM ####]
{
}

vs the current
[####]
{
}

would that still be required?
no, I am open to any suggestions that address the above issues. The reason I preferred the SECTIONKEY TYPE ID approach was it clearly indicated a way to meet the above item # 1, and it didn't seem to have any drawbacks over the [ ] approach. In otherwords, what did the [] gain? I could easily extraplate what the SECTION ITEM ### provided, with no loss. But there appears to be a lot of emotion in the [], so that approach is fine, if an extenstion is found that provides item # 1 in the confines of [].
As for a standardized delimiter, while the space has proved effective in the past, I have no problem with finding a good unique separator to put in between values. The only major issue is forcing all the scripts to be updated once again.
Well, it isn't effective given item # 2. For one can't tell when to parse it, without knowldege of the data being parsed. If knowledge of the data is required, then almost any method is effective.

As for requireing an update once again, yes it will. In my defense I pointed these issues out, even before .9x switched to dfn (first release supported the other format). I assume the dev team decided it wasn't desirable, or had other reasons (again, it seemed to have a lot of emotions tied up in it). But clearly, it doesn't encourage developement from outside tools, if something as parsing has to be custom built. Perhaps the hope one can have this time is there is a stated list of objectives that is to be accomplished by the format, and one can see how it is met. truthfully, I have yet to undestand what the DFN format provided over the other format (for one didn't have to use numbers for id's there either, from a "format" perspective. I would offer, that there might be other minor items that should be considered if one wants to prevent doing it "yet another time".

As for things like [ITEM ####] I'm suprised that script even still functions, as I'm pretty sure we lookup our item entries to be an exact match to whatever is in between those brackets.
Well, those are mostly in the Menu items. But yes, perusing throught the 9070 sections defined in the data/ directory (all files with a dfn extentsion), there are at least the ones I mentioned above.
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

some more examples of [entry #]

in data/dfndata/advancement/advance.dfn

[ADVANCEMENT 1], [ADVANCEMENT 2] , etc.

data/dfndata/carve/carve.dfn
[CARVE 1] , [CARVE 2] , etc.

data/dfndata/color/randomcolor.dfn
[RANDOMECOLOR 1], etc.


data/dfndata/create/alchemy.dfn
[SUBMENU 89], etc.
[MENUENTRY 90], etc.


data/dfndata/harditems/hardiitems.dfn

for the door entry , there is
[0x06750x06770x67d....] Although not the same consistency issue, a differnt one. In this case, it is clearly not looking for a match, but a submatch. Again, destroying generic parsing/storing/retrieveing.


data/dfndata/location/ocoloo.dfn
[LOCATION 100], etc.


Plenty of others.
giwo
Developer
Posts: 1780
Joined: Fri Jun 18, 2004 4:17 pm
Location: California
Has thanked: 0
Been thanked: 0

Post by giwo »

Ahh, I see... basically anywhere we don't use a string.

I do not like this style of script at all
[SECTION ITEM ###]

For the most part, I dislike this because (barring automatically splitting the string) we would need to do a substring match, which to my mind is slower than a 1-1 comparison. Perhaps something more like

SECTION ITEM [####]
or even just
ITEM [####]

where we capture everything inside of the brackets (allowing for strings to look however we like) and use the ITEM tag to recognize it as an ITEM.

The delimiter is really up for anyone to state, whichever is easiest it really doesn't make a difference to me, I can just as easily type ;; as , as a space. In this matter I agree, as the scripts are largely handled on a by-script basis, something we should work at changing.
Scott
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

giwo wrote:Ahh, I see... basically anywhere we don't use a string.

I do not like this style of script at all
[SECTION ITEM ###]

For the most part, I dislike this because (barring automatically splitting the string) we would need to do a substring match, which to my mind is slower than a 1-1 comparison.
Well, I had dropped the brackets (for they didn't add anything). However, I don't undertand your comment. The goal of the parser, is once it is set, it will parse it for everyone, so it never needs to be seen in that format (but will be parsed into the seperate components. So there is never a substring compare. The advantage of "SECTION" or "DATA" is it gives you one more area to expand (say you want to do something else then "DEFINE" (so perhaps "DEFINE" is the good keyword).

You could have this:
DEFINE ITEM leather_bag
{



}

This would be parsed for you, and the only match one would care about is leather_bag.


Perhaps something more like

SECTION ITEM [####]
or even just
ITEM [####]

where we capture everything inside of the brackets (allowing for strings to look however we like) and use the ITEM tag to recognize it as an ITEM.
One loses one level of abstraction, so later if one wants to embedded commands, like to "DELETE ITEM [####] ", or whatever, one looses it. the format can only be for definitations (which is ok, just thinking expansion).

is the concept that the ##### can be multiple words (whitespace allowed)?

Is it also far to make the section name, and the tags in the section, case insenstiive?
The delimiter is really up for anyone to state, whichever is easiest it really doesn't make a difference to me, I can just as easily type ;; as , as a space. In this matter I agree, as the scripts are largely handled on a by-script basis, something we should work at changing.
Well pick one.

I didnt hear what one does about the other files, where they have things like a list of hex numbers in them, etc.


Bascially, one has to decide on what one wants the section name ([####]) to do , what all it is suppose to cover. That I don't know, and am hoping one can expound on it.
giwo
Developer
Posts: 1780
Joined: Fri Jun 18, 2004 4:17 pm
Location: California
Has thanked: 0
Been thanked: 0

Post by giwo »

While the concept of multiple-word strings is interesting, I'd say it's probably best to avoid, given it will certainly cause issues later on.

Single-word strings (or multi-word with a _ or - in between are fine).

DEFINE ITEM leather_bag
{
}

looks good to me.

Parsing the string to get rid of substring searches is good, in the past we did not do so (and thus every time we looked for an item it was a substring search).

I preferr to avoid case-sensetivity (I know, you linux users hate me ;) ) whenever possible. Personally I consider it limiting (as to me, define item leather_bag should read exactly the same as DEFINE ITEM LEATHER_BAG, besides, we will probably uppercase it as soon as we read it in anyhow ;0 ).

;; as a section divider works fine, it's unique and virtually impossible to accidentally duplicate in a definition.

As for lists, those are a holdover from the old formats and ancient UOX days. I would say we make lists (all lists) generic and function the same way. IE:
DEFINE LIST names_with_a
{
listentry=albert
listentry=alhambra
}

something similar to that should suit our purposes, and make a list easy to identify. Aside from that, one will just need to know the name of a list to be certain it's the kind of list he's looking for. IE:

DEFINE LIST colorlist
{
listentry=0x0000
}

It's still a list, read and handled the same way (though in this case it's an integer), but it stores entirely different data, causing the need for at least some intelligence not in parsing but in calling / using.
Scott
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

Ok, so basically, I need to adjust the paser to:


map tags and id's (DEFINE ITEM id <<<<< that id) to upper case. Then ensure I have the find() mape the search item to upper case.

The paser will need to know what "section", so will let one specifiy that (in the default case, DEFINE).

Now, the last thing, is the define types. Again, map to upper case, and store internally seperate?


Or, should that be a submitted parameter list to the parser (to say, looking for these type of section types)?



Lastly, the parser is built with the NSPR, so directory parsing is handled via methods (so no OS issues). So if anyone wants to reuse the paser, it will need the NSPR library.



The last thing is the data. The largest change will be the multivalue parameters getting the ;;, as that is data senstive in its current form.
That, and how the current [] that have multi tags, and non tags in them will be handled.
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

Ihave converted the builk of the dfn data to the proposed format (for now, i have the old .scp tag, to tell the difference between the two files).

Does anyone want these?
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

I have classes for a sectionstore (stores section data, parses directories, and allows one to retrive a section), section, scpparser (the generic parser that you extend for you needs), and the storeparser (an extended scpparser for the secstore).

Not sure if this is useful, but here is the scpparser:

scpparser.h

Code: Select all

#ifndef __SCPPARSER_H
#define __SCPPARSER_H
#include "ustring.h"
#include <deque>
struct entry_line
{
	UString tag ;
	std::deque<UString> values ;
};

class SCPParser
{

public:
	SCPParser() ;
	bool parse(UString) ;
	bool parse(const char*) ;
	virtual void section(UString&,UString&) ;
	virtual void startSection(UString&, UString&) ;
	virtual void abortSection(UString&,UString&) ;
	virtual void entrySection(UString&,UString&,entry_line&);
	virtual void endSection(UString&,UString&) ;
protected:
	UString strip( UString&);
	bool checkSection(UString&,UString&,UString&,UString="DEFINE") ;
	bool checkStart(UString&) ;
	bool checkEnd(UString&) ;
	void entry(UString&,UString&,UString&) ;


};



#endif

and the scpparser.cpp

Code: Select all

#include <fstream>
#include "scpparser.h"

SCPParser::SCPParser()
{

}

UString SCPParser::strip( UString& line)
{
	return line.section( "//", 0, 0).simplifyWhiteSpace() ;
}

bool SCPParser::checkSection(UString &line, UString &type, UString &id,UString verb)
{
	bool rvalue = false ;
	if (line.section( " ", 0, 0).upper()== verb.upper())
	{
		rvalue= true ;
		type= line.section( " ", 1, 1).upper() ;
		id = line.section( " ", 2).upper() ;
	}
	return rvalue ;
}

bool SCPParser::checkStart(UString& line)
{
	bool rvalue = false ;
	if (line.substr(0,1) == "{")
	{
		rvalue = true ;
	}
	return rvalue ;
}

bool SCPParser::checkEnd(UString& line)
{
	bool rvalue = false ;
	if (line.substr(0,1) == "}")
	{
		rvalue = true ;
	}
	return rvalue ;

}

void SCPParser::entry(UString &type, UString &id, UString &line)
{
	entry_line linedata ;
	linedata.tag = line.section( "=",0,0).simplifyWhiteSpace().upper() ;
	UString rest = line.section( "=", 1).simplifyWhiteSpace() ;
	int count = rest.sectionCount( ";;") ;
	for (int i=0 ; i < count ; ++i)
	{
		UString temp = rest.section( ";;", i, i).simplifyWhiteSpace() ;
		linedata.values.push_back(temp) ;
	}
	entrySection(type,id,linedata) ;
}

bool SCPParser::parse( const char *input)
{
	UString temp = input ;
	return parse( temp) ;
}

bool SCPParser::parse( UString input)
{
	bool rvalue = false ;
	char orig[2048] ;
	UString line ;
	UString type,id ;
	UString currenttype,currentid ;
	int state = 0 ;
		
	std::fstream fData ;
	fData.open(input.c_str(),std::ios_base::in) ;
	if (fData.is_open())
	{
		rvalue = true ;
		while (fData.good())
		{
			std::memset(orig,0,2048) ;
			fData.getline(orig,2048) ;
			if (fData.good())
			{
				line = orig ;
				
				
				line = strip(line) ;
				if (checkSection(line,type,id)) ;
				{
					if (state ==0)
					{
						section(type,id) ;
						state = 1 ;
						currenttype=type ;
						currentid = id ;
					}
					else if (state ==1)
					{
						// We never started the other section, abort it
						if (currenttype != "" && currentid !="")
							abortSection(currenttype,currentid) ;
						section(type,id) ;
						currenttype = type ;
						currentid = id ;
						state = 1 ;
					}
					else
					{
						// We never properly closed the other section, so close it
						endSection(currenttype,currentid) ;
						section(type,id) ;
						currenttype = type ;
						currentid = id ;
						state = 1 ;
					}
				}
				if (checkStart(line))
				{
					if(state == 1)
					{
						startSection(currenttype,currentid) ;
						state = 2 ;
					}
					line ="" ;

				}
				if (checkEnd(line))
				{
					if (state ==1)
					{
						// We where looking for a start, abort the section
						if (currenttype != "" && currentid !="")
							abortSection(currenttype,currentid) ;
						currenttype="";
						currentid="" ;
						state = 0 ;
					}
					else if (state ==2)
					{
						endSection(currenttype,currentid) ;
						state=0 ;

					}
					line ="";
				}
				if (line !="")
					entry(currenttype,currentid,line) ;

			}
		}
		// See if we have any hanging sectionss
		if (state ==2)
		{
			endSection(currenttype,currentid) ;
		}
		else if (state == 1)
		{
			if (currenttype != "" && currentid !="")
				abortSection(currenttype,currentid) ;

		}
		fData.close() ;
	}
	return rvalue ;
}
One just fills in the virutal functions to act when fired.
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

I will be out for a few days.

I have the following classes if they are of interest:
entryline.h

Code: Select all

#ifndef __ENTRYLINE_H
#define __ENTRYLINE_H

struct entry_line
{
	UString tag ;
	std::deque<UString> values ;
};

#endif
Section .h

Code: Select all

#ifndef __SECTION_H
#define __SECTION_H

#include "ustring.h"
#include <deque>
#include "entryline.h"



class Section
{
	friend class MultiSection ;
public:
	Section() ;
	Section(const Section&) ;
	~Section( );
	UString title( );
	UString type( ) ;
	void clear();
	std::size_t size();
	std::deque<entry_line>& data();
	bool find(UString &, entry_line &, int =0 ) ;
	bool find(const char *, entry_line &, int =0) ;
protected:
	std::deque<entry_line> mydata ;
	UString mytitle ;
	UString mytype ;

};

#endif
section.cpp

Code: Select all

#include "section.h"
Section::Section( )
{

}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Section::Section( const Section& input)
{
	mytitle = input.mytitle;
	mydata = input.mydata ;
	mytype = input.mytype ;

}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Section::~Section()
{
	clear();

}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
UString Section::title( )
{
	return mytitle ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
UString Section::type( )
{
	return mytype ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Section::clear()
{
	mytitle ="";
	mydata.clear();
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

std::size_t Section::size()
{
	return mydata.size() ;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
std::deque<entry_line>& Section::data()
{
	return mydata ;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++
bool Section::find( const char *key, entry_line &data, int offset)
{
	UString temp = key ;
	return find( temp, data, offset) ;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++
bool Section::find( UString &key, entry_line &data, int offset)
{
	std::deque<entry_line>::iterator iter = mydata.begin() ;
	int count=0 ;
	bool rvalue = false ;
	while (iter != mydata.end())
	{
		if ((*iter).tag == key.upper())
		{
			if (count < offset)
			{
				++count ;
			}
			else
			{
				data = *iter ;
				
				rvalue = true ;
				break;
			}
		}
		++iter ;
	}

	return rvalue ;

}

SecStore.h

Code: Select all

#ifndef __SECSTORE_H
#define __SECSTORE_H

#include "section.h"
#include <list>

class SecStore
{
	friend class MultiSection ;
public:
	SecStore();
	SecStore(const SecStore&);
	virtual ~SecStore( ) ;
	UString type( ) ;
	bool deleteSection(UString&) ;
	bool find(UString&,Section*);
	bool find(const char*,Section*) ;
	bool like(UString&,Section*) ;
	bool like(const char*,Section*) ;
	void clear() ;
	std::size_t size() ;
protected:
 
	std::list<Section*> mydata ;
	UString mytype ;

};





#endif
SecStore.cpp

Code: Select all

#include "secstore.h"

#include <queue>
#include <iostream>

SecStore::SecStore( )
{


}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++
SecStore::SecStore( const SecStore& input)
{
	std::list<Section*> cpy = input.mydata ;
	// Loop through, make copies, and store
	std::list<Section*>::iterator iter = cpy.begin() ;

	while ( iter != cpy.end() )
	{
		Section *temp = new Section( (**iter)) ;
		mydata.push_back(temp) ;

		++iter ;
	}
	mytype = input.mytype ;

}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SecStore::~SecStore()
{
	clear() ;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void SecStore::clear()
{
	
	std::list<Section*>::iterator iter = mydata.begin() ;
	while ( iter != mydata.end() )
	{
	   delete *iter ;
		++iter ;
	}
	mytype="";
    

}
//++++++++++++++++++++++++++++++++++++++++++++++++++
UString SecStore::type( )
{
	return mytype ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++
bool SecStore::find( const char *input,  Section *sec)
{
	UString temp = input ;
	return find( temp, sec) ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool SecStore::find(UString &title, Section *sec) 
{
	// temp stuff

	sec = NULL ;
	bool rvalue = false ;
	UString tmptitle = title.upper() ;
	std::list<Section*>::iterator iter = mydata.begin() ;
	while ( iter != mydata.end() )
	{
		if ( (**iter).title() == tmptitle )
		{
			// Found, so setup
			rvalue = true ;
			sec = *iter ;
			break;
		}
		++iter ;

	}
	return rvalue ;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++
std::size_t SecStore::size()
{
	return mydata.size() ;

}

//++++++++++++++++++++++++++++++++++++++++++++++++++
bool SecStore::like( const char *input,  Section *sec)
{
	UString temp = input ;
	return like( temp, sec) ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool SecStore::like(UString &title, Section *sec) 
{
	// temp stuff
	UString tmptitle = title.upper() ;
	sec = NULL ;
	bool rvalue = false ;
	std::list<Section*>::iterator iter = mydata.begin() ;
	while ( iter != mydata.end() )
	{
		if ( (**iter).title().find( tmptitle) != std::string::npos )
		{
			// Found, so setup
			rvalue = true ;
			sec = *iter ;
			break;
		}
		++iter ;

	}
	return rvalue ;
}

//++++++++++++++++++++++++++++++++++++++++++++++++
bool SecStore::deleteSection(UString& input)
{
	bool rvalue = false  ;
	std::list<Section*>::iterator iter = mydata.begin() ;
	while ( iter != mydata.end() )
	{
		if ( (**iter).title() == input )
		{
			// Found, so setup
			rvalue = true ;
			Section* sec = *iter ;
			mydata.erase(iter) ;
			delete sec ;
			break;
		}

		++iter ;
	}
	return rvalue ;
}

scpparser.h

Code: Select all

#ifndef __SCPPARSER_H
#define __SCPPARSER_H
#include "ustring.h"
#include <deque>
#include "entryline.h"
class SCPParser
{

public:
	SCPParser() ;
	virtual ~SCPParser() ;
	bool parse(UString) ;
	bool parse(const char*) ;

protected:
	virtual void section(UString&,UString&) =0;
	virtual void startSection(UString&, UString&)=0 ;
	virtual void abortSection(UString&,UString&) =0;
	virtual void entrySection(UString&,UString&,entry_line&)=0;
	virtual void endSection(UString&,UString&) =0;
protected:
	UString strip( UString&);
	bool checkSection(UString&,UString&,UString&,UString="DEFINE") ;
	bool checkStart(UString&) ;
	bool checkEnd(UString&) ;
	void entry(UString&,UString&,UString&) ;


};



#endif
scpparser.cpp

Code: Select all

#include <fstream>
#include "scpparser.h"

SCPParser::SCPParser()
{

}
SCPParser::~SCPParser()
{

}

UString SCPParser::strip( UString& line)
{
	return line.section( "//", 0, 0).simplifyWhiteSpace() ;
}

bool SCPParser::checkSection(UString &line, UString &type, UString &id,UString verb)
{
	bool rvalue = false ;
	if (line.section( " ", 0, 0).upper()== verb.upper())
	{
		rvalue= true ;
		type= line.section( " ", 1, 1).upper() ;
		id = line.section( " ", 2).upper() ;
	}
	return rvalue ;
}

bool SCPParser::checkStart(UString& line)
{
	bool rvalue = false ;
	if (line.substr(0,1) == "{")
	{
		rvalue = true ;
	}
	return rvalue ;
}

bool SCPParser::checkEnd(UString& line)
{
	bool rvalue = false ;
	if (line.substr(0,1) == "}")
	{
		rvalue = true ;
	}
	return rvalue ;

}

void SCPParser::entry(UString &type, UString &id, UString &line)
{
	entry_line linedata ;
	linedata.tag = line.section( "=",0,0).simplifyWhiteSpace().upper() ;
	UString rest = line.section( "=", 1).simplifyWhiteSpace() ;
	int count = rest.sectionCount( ";;") ;
	for (int i=0 ; i < count ; ++i)
	{
		UString temp = rest.section( ";;", i, i).simplifyWhiteSpace() ;
		linedata.values.push_back(temp) ;
	}
	entrySection(type,id,linedata) ;
}

bool SCPParser::parse( const char *input)
{
	UString temp = input ;
	return parse( temp) ;
}

bool SCPParser::parse( UString input)
{
	bool rvalue = false ;
	char orig[2048] ;
	UString line ;
	UString type,id ;
	UString currenttype,currentid ;
	int state = 0 ;
		
	std::fstream fData ;
	fData.open(input.c_str(),std::ios_base::in) ;
	if (fData.is_open())
	{
		rvalue = true ;
		while (fData.good())
		{
			std::memset(orig,0,2048) ;
			fData.getline(orig,2048) ;
			if (fData.good())
			{
				line = orig ;
				
				
				line = strip(line) ;
				if (checkSection(line,type,id)) ;
				{
					if (state ==0)
					{
						section(type,id) ;
						state = 1 ;
						currenttype=type ;
						currentid = id ;
					}
					else if (state ==1)
					{
						// We never started the other section, abort it
						if (currenttype != "" && currentid !="")
							abortSection(currenttype,currentid) ;
						section(type,id) ;
						currenttype = type ;
						currentid = id ;
						state = 1 ;
					}
					else
					{
						// We never properly closed the other section, so close it
						endSection(currenttype,currentid) ;
						section(type,id) ;
						currenttype = type ;
						currentid = id ;
						state = 1 ;
					}
				}
				if (checkStart(line))
				{
					if(state == 1)
					{
						startSection(currenttype,currentid) ;
						state = 2 ;
					}
					line ="" ;

				}
				if (checkEnd(line))
				{
					if (state ==1)
					{
						// We where looking for a start, abort the section
						if (currenttype != "" && currentid !="")
							abortSection(currenttype,currentid) ;
						currenttype="";
						currentid="" ;
						state = 0 ;
					}
					else if (state ==2)
					{
						endSection(currenttype,currentid) ;
						state=0 ;

					}
					line ="";
				}
				if (line !="")
					entry(currenttype,currentid,line) ;

			}
		}
		// See if we have any hanging sectionss
		if (state ==2)
		{
			endSection(currenttype,currentid) ;
		}
		else if (state == 1)
		{
			if (currenttype != "" && currentid !="")
				abortSection(currenttype,currentid) ;

		}
		fData.close() ;
	}
	return rvalue ;
}
multisection.h

Code: Select all

#ifndef __MULTISECTION_H
#define __MULTISECTION_H

#include "scpparser.h"
#include <map>
#include "secstore.h"

class MultiSection : public SCPParser
{
public:
	MultiSection() ;
	virtual ~MultiSection();

	bool processDir(UString&);
	std::size_t size() ;
	// sectionstorage
	bool find(UString&,SecStore*) ;
	void delStorage(UString&) ;

	// sections
	bool find(UString&,UString&,Section*) ;
	bool like(UString&,UString&,Section*) ;

	void clear() ;




protected:
	virtual void section(UString &, UString &) ;
	virtual void abortSection(UString &, UString &);
	virtual void startSection(UString &, UString &);
	virtual void endSection(UString &, UString &);
	virtual void entrySection(UString &, UString &, entry_line &);

protected:
	std::map<UString,SecStore*> storage ;
	Section *currentsection ;
};

#endif
multisection.cpp

Code: Select all

#include "multisection.h"
#include <prio.h>
#include <queue>

MultiSection::MultiSection()
{
	currentsection = NULL;
}
MultiSection::~MultiSection()
{
	clear();
}

void MultiSection::section(UString &type, UString &id)
{
	currentsection = new Section ;
	(*currentsection).mytype = type ;
	(*currentsection).mytitle = id ;
}
void MultiSection::abortSection(UString &, UString &)
{
	if (currentsection != NULL)
	{
		delete currentsection ;
		currentsection = NULL;
	}
}
void MultiSection::startSection(UString &, UString &)
{
	// don't need to do anything here
}
void MultiSection::endSection(UString &type, UString &id)
{
	std::map<UString,SecStore*>::iterator iter ;
	SecStore *temp ;
	iter = storage.find(type) ;
	if (iter == storage.end())
	{
		temp = new SecStore ;
		(*temp).mytype = type ;
		storage.insert(std::make_pair(type,temp)) ;

	}
	else
		temp = iter->second ;
	// Have the storage container, add the section
	(*temp).mydata.push_back(currentsection) ;
	currentsection = NULL ;
}
void MultiSection::entrySection(UString &type, UString &id, entry_line &input)
{
	if (currentsection != NULL)
	{
		(*currentsection).mydata.push_back(input) ;
	}
}

void MultiSection::clear()
{
	std::map<UString,SecStore*>::iterator iter = storage.begin() ;
	while (iter != storage.end())
	{
		delete iter->second ;
		++iter ;
	}
}

std::size_t MultiSection::size()
{
	return storage.size() ;
}
bool MultiSection::processDir(UString& input)
{
	std::queue<UString> files ;
	std::queue<UString> directories ;

	bool rvalue = false ;
	PRDir *dir= PR_OpenDir(input.c_str());
	if ( dir != NULL )
	{
		rvalue = true ;

		PRDirEntry *tdir = PR_ReadDir(dir,PR_SKIP_HIDDEN );
		// Note, we don't have to cleanup tdir, closedir.readdir will do that
		if ( tdir != NULL )
		{
			while ( tdir !=NULL )
			{

				UString temp =input + "/" ;
				temp += (*tdir).name ;
				// Determine what this is, file or directory
				PRFileInfo info;
				PRStatus status= PR_GetFileInfo(temp.c_str(),&info);
				if ( status == PR_SUCCESS )
				{
					if ( info.type ==PR_FILE_FILE )
					{
						// Check the extension!
						if ( temp.length() >=4 )
						{
							UString ext = temp.substr(temp.length()-4,4);
							ext = ext.upper() ;
							if ( ext == ".SCP" )
							{
								files.push(temp) ;
							}
						}


					}
					else if ( info.type ==PR_FILE_DIRECTORY )
					{
						directories.push(temp);
					}


				}
				tdir = PR_ReadDir(dir,PR_SKIP_BOTH );
			}
			PR_CloseDir(dir) ;

			// Work off the queues
			while ( files.size() !=0 )
			{
				UString file = files.front() ;
				files.pop() ;

				parse(file) ;
			}
			while ( directories.size()!=0 )
			{
				UString directory = directories.front() ;
				directories.pop() ;
				processDir(directory) ;
			}

		}
	}


	return rvalue ;
}

Multisection has a few more methods I have to do for it. But hopefully one can see, that one can now generically parse the data files, and retrieve sections or entries, case insenstive , and get back an all ready parsed line into the tag, and values (if any). One can retrive multiple entries of the same tag (if present) as well.
xir
UOX3 Neophyte
Posts: 47
Joined: Mon Aug 23, 2004 2:59 pm
Has thanked: 0
Been thanked: 0

Post by xir »

Just wondering you maybe might want to convert the file i/o to NSPR. This would standardise the way filepaths were handled within the whole system.
NSPR converts the slashes in a pathname to the directory separator of the native OS--for example, backslash (\) on Windows and colon (: ) on Mac OS--before passing it to the native system calls.
Good job though ! :)
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Post by punt »

I used NSPR for the directory, but wanted theparser to be as free of library use as possible(for tool use as well).

NSPR for file didn't really buy anything, as fstream will open "/" on both platforms anyway. So if one really wanted to standardize, they could,just usethe "/".
giwo
Developer
Posts: 1780
Joined: Fri Jun 18, 2004 4:17 pm
Location: California
Has thanked: 0
Been thanked: 0

Post by giwo »

I thought this might be of interest for those who haven't had a chance to look at it.....
Scott
Post Reply