--- /dev/null
+libmspdatafile - structured datafile library
+Copyright © 2006-2007 Mikko Rasa, Mikkosoft Productions
+Version 0.1 readme
+
+
+Libmspdatafile is a library for reading and writing structured data files. It
+supports both a human-readable text format and a more compact binary format.
+The text-based format is designed to be compact yet clean, avoiding much of the
+redundancy of XML.
+
+Libmspdatafile is distributed under the terms of the GNU Lesser Public License.
+Full text of the license can be found in the file License.txt.
+
+
+*** Structure
+
+The basic building block of a datafile is a statement. A statement has a
+keyword and zero or more arguments. It may also have zero or more substate-
+ments.
+
+The interface is heavily object-oriented and typically one statement in a file
+represents one object in the program's memory.
+
+The library supports five basic data types: integers, floats, strings, booleans
+and enums. Data types are strictly enforced - the only allowed conversion is
+from float to integer.
+
+The correct argument types for a statement is determined from the function or
+variable used to load it. This means that semantical validation can only be
+done by the program that actually uses the file. A generic tool can only
+perform syntactic validation.
+
+
+*** Basic usage
+
+Loading data from files is achieved through Loader classes. Such a class must
+be derived from the DataFile::Loader class. In the constructor, a series of
+calls to the various overloads of the add() function is executed, telling the
+loader what to do. The two basic actions are calling a function of the loader
+with the statement's arguments, or assigning the arguments to variables of the
+loaded object.
+
+There is a load() function in namespace scope that allows loading a whole file
+into an object.
+
+It is also possible to call the Parser::parse function directly and obtain a
+raw Statement. However, this is rarely useful.
+
+
+*** Syntax
+
+The syntax of the text format vaguely resembles C. Statements are terminated
+with a semicolon and substatements are enclosed between burly braces. The sub-
+statement block comes after all arguments and the semicolon comes after it. An
+empty substatement block is allowed and equal to an absent block.
+
+Integers can be written in decimal, octal or hexadecimal notation. A minus
+sign can be used in any base for negative numbers, as well as an optional plus
+sign for positive numbers.
+
+Floats must be written in decimal and they must contain a decimal point. An
+exponent may come after the mantissa, separated by the character e. Minus and
+plus signs are allowed in both exponent and mantissa.
+
+Strings are enclosed in double quotes. Backslash can be used to escape a
+literal double quote or backslash.
+
+Booleans are either true or false.
+
+Enumeration values are represented as symbolic identifiers. They may contain
+alphaumeric characters and underscores and must begin with a letter or an
+underscore.
+
+
+*** Binary format
+
+The binary format is not intended to be manipulated by anything else than
+libmspdatafile. A detailed description is not available at this time.
+
+The tool provided with the library can be used to convert between the two
+formats.
+
+
+*** $Id$
namespace Msp {
namespace DataFile {
+/**
+Stores statement information for binary files.
+*/
struct DictEntry
{
std::string keyword;
ParserMode(i, s),
first(true)
{
- dict[1]=DictEntry("__kw", "iss");
+ dict[1]=DictEntry("__st", "iss");
dict[2]=DictEntry("__enum", "is");
}
while(1)
{
Statement st=parse_statement();
- if(st.keyword=="__kw")
+ if(st.keyword=="__st")
{
if(st.args.size()!=3)
throw TypeError(src+": Keyword definition must have three arguments");
namespace Msp {
namespace DataFile {
+/**
+Parses data in binary format.
+*/
class BinaryParser: public ParserMode
{
private:
next_st_id(3),
next_enum_id(1)
{
- dict[DictEntry("__kw", "iss")]=1;
+ dict[DictEntry("__st", "iss")]=1;
dict[DictEntry("__enum", "is")]=1;
}
if(!dict.count(de))
{
Statement kst;
- kst.keyword="__kw";
+ kst.keyword="__st";
kst.args.push_back(next_st_id);
kst.args.push_back(de.keyword);
kst.args.push_back(de.args);
namespace Msp {
namespace DataFile {
+/**
+Writes data in binary format.
+*/
class BinaryWriter: public WriterMode
{
private:
/**
-Base class for data loaders. To enable objects of a certain class to be loaded
-from datafiles, create a public Loader class in it, derived from this class.
-Typically the Loader class contains a reference to the object being loaded. If
-you want to load data members of the object directly, the Loader class must
-have a member function get_object() returning that reference.
+Base class for data loaders. To give loading capabilities to a class, create a
+public Loader class in it, derived from this class. Typically a loader object
+contains a reference to the loaded object. To make use of loading directly
+into data members, the Loader class must have a get_object() member function,
+returning that reference.
*/
class Loader
{
protected:
Loader(): cur_st(0) { }
+ /**
+ Adds a keyword that is loaded with a zero-argument function.
+ */
template<typename L>
void add(const std::string &k, void (L::*func)())
{ actions.insert(typename ActionMap::value_type(k, new LoaderFunc0<L>(func))); }
void add(const std::string &k, void (L::*func)(A0, A1, A2, A3))
{ actions.insert(typename ActionMap::value_type(k, new LoaderFunc4<L, A0, A1, A2, A3>(func))); }
+ /**
+ Adds a keyword that is loaded into a variable of the loaded object.
+ */
template<typename L, typename T0>
void add(const std::string &k, T0 L::*p0)
{ actions.insert(typename ActionMap::value_type(k, new LoadValue1<L, T0>(p0))); }
void add(const std::string &k, T0 L::*p0, T1 L::*p1)
{ actions.insert(typename ActionMap::value_type(k, new LoadValue2<L, T0, T1>(p0, p1))); }
+ /**
+ Adds a keyword that is recognized but ignored.
+ */
void add(const std::string &k)
{ actions.insert(ActionMap::value_type(k, 0)); }
class Statement;
struct Token;
+/**
+Frontend for loading datafiles. Handles switching between text and binary
+formats. A Parser evaluates into a boolean value indicating whether more
+statements may be read.
+*/
class Parser
{
-public:
- Parser(std::istream &, const std::string &);
- ~Parser();
-
- Statement parse();
- operator bool() const { return in; }
private:
Input in;
std::string src;
bool good;
ParserMode *mode;
+
+public:
+ Parser(std::istream &i, const std::string &s);
+ ~Parser();
+
+ /**
+ Reads a statement from the input. If the end of input was reached, an empty
+ invalid statement will be returned. If an error occurs, the parser will be
+ marked as bad and no more statements may be read, even if the exception was
+ caught.
+ */
+ Statement parse();
+
+ operator bool() const { return good && in; }
};
} // namespace DataFile
class Input;
+/**
+Base class for parse modes.
+*/
class ParserMode
{
protected:
class Statement;
class WriterMode;
+/**
+Frontend for writing data.
+*/
class Writer
{
private:
bool binary;
public:
- Writer(std::ostream &);
- void write(const Statement &);
- void set_binary(bool);
+ Writer(std::ostream &o);
+
+ /**
+ Writes a statement to the output. This function always writes a complete
+ statement, so it's not possible to add substatements later.
+ */
+ void write(const Statement &st);
+
+ /**
+ Sets binary or text mode. While it is possible to enter and exit binary
+ mode multiple times, doing so produces sub-optimal output.
+
+ @param b true for binary mode, false for text
+ */
+ void set_binary(bool b);
};
} // namespace DataFile