Code:
#include <cctype> // std::isspace std::isalpha std::isdigit
#include <fstream> // std::ifstream
#include <iostream> // std::cout std::cerr
#include <sstream> // std::istringstream
#include <string> // std::string
// a "dumb" parser (technically this is only a simple exclusive lexer or
// tokenizer relying on additional semantic information from higher level
// routines for actual parsing) based on `std::string' iterators
// ignores extra whitespace characters ('\n' '\t' '\v' '\b' '\r' '\f' ' ')
// between parsing tokens
namespace parser
{
// we use this `typedef' exclusively
typedef std::string::iterator citerator;
// this structure stores the results of every
// funtion in our parser library
struct result_type
{
bool success; // whether of not the individual parse is successful (if success is true the other members are valid)
citerator cursor; // an iterator reference to the first character to process
citerator end; // an iterator reference to the next element after the last element to process
};
// this function, used by `variable_name' below, determines
// the set of characters valid for a variable name which
// consists of uppercase letters (A-Z), lowercase
// letters (a-z), numbers (0-9), and the underscore (_)
bool isvvnchar
(
char character
)
{
return(std::isalnum(character) || '_' == character);
}
// parses a variable name where a variable name
// starts with a letter character and terminates
// with an invalid character as determined by the above
// `isvvnchar' function
result_type variable_name
(
citerator cursor,
const citerator end // we don't want to change this by accident
)
{
// setup the return value in case of failure
result_type result = {false, cursor, end};
// trim beginning whitespace characters
while(std::isspace(*cursor) && end != cursor)
{
++cursor;
}
// the first character must be a character
if(std::isalpha(*cursor))
{
// update the result iterator in case of success
result.cursor = cursor;
while(isvvnchar(*cursor) && end != cursor)
{
++cursor;
}
// so far we have a valid name
// it is not the job of this function to determine
// if the result is meaningful if it could be a
// name it is a name and we have succeeded
// `cursor' currently references the next character
// after the last character match (from `isvvnchar')
// or `end' which matches our definition of a good
// result as above
result.end = cursor;
// finally we need to flag the result as successful
result.success = true;
}
return(result);
}
// parses integer values (kinda duh)
result_type integer_value
(
citerator cursor,
const citerator end
)
{
result_type result = {false, cursor, end};
while(std::isspace(*cursor) && end != cursor)
{
++cursor;
}
if(std::isdigit(*cursor))
{
result.cursor = cursor;
while(isdigit(*cursor) && end != cursor)
{
++cursor;
}
result.end = cursor;
result.success = true;
}
return(result);
}
// this function, used by `name_value' below, determines
// the set of characters valid for a name value which
// consists of uppercase letters (A-Z), lowercase
// letters (a-z), the space ( ) character
bool isvsvchar
(
char character
)
{
return(std::isalpha(character) || ' ' == character);
}
// trims beginning spaces so the name must start with a
// letter
result_type name_value
(
citerator cursor,
const citerator end
)
{
result_type result = {false, cursor, end};
while(std::isspace(*cursor) && end != cursor)
{
++cursor;
}
if(isvsvchar(*cursor))
{
result.cursor = cursor;
while(isvsvchar(*cursor) && end != cursor)
{
++cursor;
}
result.end = cursor;
result.success = true;
}
return(result);
}
// this function parses the assignment portion of the line
result_type assignment
(
citerator cursor,
const citerator end
)
{
result_type result = {false, cursor, end};
while(std::isspace(*cursor) && end != cursor)
{
++cursor;
}
if('=' == *cursor)
{
result.cursor = cursor;
++cursor;
result.end = cursor;
result.success = true;
// if at this point you've noticed a strong
// resemblance between the four parsing function
// you should congratulate yourself
// there exists a straightforward way
// of merging the shared source of these
// functions for this sort of parser
// allowing you to build more "elemental
// parsers from simple functions
// that define the range and domain
// of the function in question
}
return(result);
}
}
struct character_sheet
{
std::string cName;
int cLeftOver;
int cStr;
int cIntel;
int cAgi;
int cCon;
int cSpe;
int cNin;
int cPoints;
};
// for now only allows one character per file
// more work needed to allow more than one character
// per file
// overwrites the old character if additional statements are found
void parse_character
(
std::ifstream & fin,
character_sheet & entity
)
{
using namespace std;
int linenum(0);
string line;
// get a line from the file
while(getline(fin, line))
{
int statementnum(0);
string statement;
istringstream sin(line);
++linenum;
// build a statement line using our delimiter (;)
// and loop until there are no more statements
while(getline(sin, statement, ';'))
{
using namespace parser;
result_type result;
++statementnum;
result = variable_name(statement.begin(), statement.end());
if(true == result.success)
{
// construct a `string' variable from the parsed information
// that the constructor for a `string' accepts these
// parameters is no accident (it is what we wanted)
string variable(result.cursor, result.end);
// compare the variable with what we understand (semantic)
if("cName" == variable)
{
// remember from above
// the `end' member of `result_type'
// references one element beyond the last
// element parsed by the function
// that generated the instance
// in other words, we use the "old" result
// to continue parsing
// we have a variable name
// we need to see if we have a valid
// assignment statement so we obviously
// try to parse that next
result = assignment(result.end, statement.end());
if(true == result.success)
{
// we have an assignment statement
// now we need to parse the value being assigned
// for the "name" variable the value
// is expected to be a name value as
// determined by the parsers
result = name_value(result.end, statement.end());
if(true == result.success)
{
// we have successfully parsed a name value assignment statement
// now we assign that value to the entity
// this constructs a temporary string
// from the parsed data and assigns the result
// to the entity
entity.cName = string(result.cursor, result.end);
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
// checking for more variables assignments we understand
else if("cStr" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
// we have an assignment statement
// now we need to parse the value being assigned
// for the "cStr" variable (and the others) the value
// is expected to be a integer value as
// determined by the parsers
result = integer_value(result.end, statement.end());
if(true == result.success)
{
// we have successfully parsed an integer value statement
// now we assign that value to the entity
// this constructs a temporary `istringstring'
// from the parsed data and attempts to
// "read" the result and assign the result
// to the associated value of the entity
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cStr = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else if("cIntel" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
result = integer_value(result.end, statement.end());
if(true == result.success)
{
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cIntel = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else if("cAgi" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
result = integer_value(result.end, statement.end());
if(true == result.success)
{
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cAgi = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else if("cCon" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
result = integer_value(result.end, statement.end());
if(true == result.success)
{
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cCon = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else if("cSpe" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
result = integer_value(result.end, statement.end());
if(true == result.success)
{
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cSpe = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
// if at this point you've not seen the obvious
// repetition you should be ashamed of yourself
// there is a straightforward way
// to pull out this repetition into a function
// which can simply be called once per statement
else if("cNin" == variable)
{
result = assignment(result.end, statement.end());
if(true == result.success)
{
result = integer_value(result.end, statement.end());
if(true == result.success)
{
istringstream vin(string(result.cursor, result.end));
int tempvalue(0);
if(vin >> tempvalue)
{
entity.cNin = tempvalue;
}
else
{
cerr << "not an integer value? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
else
{
cerr << "bad assignment? (" << linenum << ',' << statementnum << ")\n";
}
}
}
else
{
cerr << "bad variable? (" << linenum << ',' << statementnum << ")\n";
}
}
}
}
int main()
{
using namespace std;
character_sheet player = {"", 0, 0, 0, 0, 0, 0, 0, 0};
ifstream fin("player.txt");
parse_character(fin, player);
cout << player.cName << '\n';
cout << player.cStr << '\n';
cout << player.cIntel << '\n';
cout << player.cAgi << '\n';
cout << player.cCon << '\n';
cout << player.cSpe << '\n';
cout << player.cNin << '\n';
return(0);
}