forked from 0ad/0ad
1090 lines
26 KiB
C++
Executable File
1090 lines
26 KiB
C++
Executable File
// last modified Thursday, May 08, 2003
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include "Parser.h"
|
|
#include "lib.h"
|
|
|
|
#pragma warning(disable:4786)
|
|
using namespace std;
|
|
|
|
//-------------------------------------------------
|
|
// Macros
|
|
//-------------------------------------------------
|
|
|
|
#define REGULAR_MAX_LENGTH 10
|
|
#define START_DYNAMIC '<'
|
|
#define END_DYNAMIC '>'
|
|
#define START_OPTIONAL '['
|
|
#define END_OPTIONAL ']'
|
|
#define REGULAR_EXPRESSION '$'
|
|
|
|
// use GetDouble and type-cast it to <<type>>
|
|
#define FUNC_IMPL_CAST_GETDOUBLE(func_name,type) \
|
|
bool CParserValue::func_name(type &ret) \
|
|
{ \
|
|
double d; \
|
|
if (GetDouble(d)) \
|
|
return ret = (type)d, true; \
|
|
else \
|
|
return false; \
|
|
}
|
|
|
|
// Function-implementation creator for GetArg%type% that will call
|
|
// Get%type% from the CParserValue
|
|
// func_name must belong to CParserFile
|
|
#define FUNC_IMPL_GETARG(func_name, get_name, type) \
|
|
bool CParserLine::func_name(const int & arg, type &ret) \
|
|
{ \
|
|
if (GetArgCount() <= (unsigned int)arg) \
|
|
return false; \
|
|
return m_Arguments[arg].get_name(ret); \
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// Function definitions
|
|
//-------------------------------------------------
|
|
|
|
static bool _IsStrictNameChar(const char& c);
|
|
static bool _IsValueChar(const char& c);
|
|
|
|
|
|
// Functions used for checking a character if it belongs to a value
|
|
// or not
|
|
|
|
// Checks ident
|
|
static bool _IsStrictNameChar(const char& c)
|
|
{
|
|
return ((c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9'));
|
|
}
|
|
|
|
// Checks value
|
|
static bool _IsValueChar(const char& c)
|
|
{
|
|
return ((c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c=='.' || c=='_');
|
|
}
|
|
|
|
// CParserValue
|
|
// ---------------------------------------------------------------------| Class
|
|
|
|
CParserValue::CParserValue()
|
|
{
|
|
}
|
|
|
|
CParserValue::~CParserValue()
|
|
{
|
|
}
|
|
|
|
// Parse the string in Value to different types
|
|
|
|
// bool
|
|
bool CParserValue::GetBool(bool &ret)
|
|
{
|
|
// TODO Raj Add or remove some? I can make it all lowercase
|
|
// first so True and TRUE also works, or you could just
|
|
// add them too
|
|
|
|
// true
|
|
if (m_String == "true" ||
|
|
m_String == "on" ||
|
|
m_String == "1" ||
|
|
m_String == "yes")
|
|
{
|
|
ret = true;
|
|
return true;
|
|
}
|
|
else
|
|
// false
|
|
if (m_String == "false" ||
|
|
m_String == "off" ||
|
|
m_String == "0" ||
|
|
m_String == "no")
|
|
{
|
|
ret = false;
|
|
return true;
|
|
}
|
|
|
|
// point only erroneous runs reach
|
|
return false;
|
|
}
|
|
|
|
// double
|
|
bool CParserValue::GetDouble(double &ret)
|
|
{
|
|
// locals
|
|
double TempRet = 0.0;
|
|
size_t Size = m_String.size();
|
|
size_t i;
|
|
bool AtLeastOne = false; // Checked if at least one of the loops
|
|
// run, otherwise "." would parse OK
|
|
size_t DecimalPos;
|
|
bool Negative = false; // "-" is found
|
|
|
|
// Check if '-' is found
|
|
if (m_String[0]=='-')
|
|
{
|
|
Negative = true;
|
|
}
|
|
|
|
// find decimal position
|
|
DecimalPos = m_String.find(".");
|
|
if (DecimalPos == string::npos)
|
|
DecimalPos = Size;
|
|
|
|
// Iterate left of the decimal sign
|
|
//
|
|
for (i=(Negative?1:0); i < DecimalPos; ++i)
|
|
{
|
|
// Set AtLeastOne to true
|
|
AtLeastOne = true;
|
|
|
|
// Check if a digit is found
|
|
if (m_String[i] >= '0' && m_String[i] <= '9')
|
|
{
|
|
double exp = (DecimalPos-i-1); // disambiguate pow() argument type
|
|
TempRet += (m_String[i]-'0')*pow(10.0, exp);
|
|
}
|
|
else
|
|
{
|
|
// parse error!
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Iterate right of the decimal sign
|
|
//
|
|
for (i=DecimalPos+1; i < Size; ++i)
|
|
{
|
|
// Set AtLeastOne to true
|
|
AtLeastOne = true;
|
|
|
|
// Check if a digit is found
|
|
if (m_String[i] >= '0' && m_String[i] <= '9')
|
|
{
|
|
double exp = (int)(DecimalPos-i); // disambiguate pow() argument type
|
|
TempRet += (m_String[i]-'0')*pow(10.0,exp);
|
|
}
|
|
// It will accept and ending f, like 1.0f
|
|
else if (!(i==Size-1 && m_String[i] == 'f'))
|
|
{
|
|
// parse error!
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!AtLeastOne)return false;
|
|
|
|
// Set the reference to the temp value and return success
|
|
ret = (Negative?-TempRet:TempRet);
|
|
return true;
|
|
}
|
|
|
|
// string - only return m_String, can't fail
|
|
bool CParserValue::GetString(std::string &ret)
|
|
{
|
|
ret = m_String;
|
|
return true;
|
|
}
|
|
|
|
bool CParserValue::GetString( CStr& ret )
|
|
{
|
|
ret = m_String;
|
|
return true;
|
|
}
|
|
|
|
// These macros include the IMPLEMENTATION of the
|
|
// the function in the macro argument for CParserValue
|
|
// They use GetDouble, and then type-cast it
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetFloat, float)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetChar, char)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetShort, short)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetInt, int)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetLong, long)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedShort, unsigned short)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedInt, unsigned int)
|
|
FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedLong, unsigned long)
|
|
|
|
// CParserTaskTypeNode
|
|
// ---------------------------------------------------------------------| Class
|
|
|
|
CParserTaskTypeNode::CParserTaskTypeNode() : m_ParentNode(NULL), m_NextNode(NULL), m_AltNode(NULL)
|
|
{
|
|
}
|
|
|
|
CParserTaskTypeNode::~CParserTaskTypeNode()
|
|
{
|
|
}
|
|
|
|
// Delete all children
|
|
void CParserTaskTypeNode::DeleteChildren()
|
|
{
|
|
// Delete nodes if applicable
|
|
if (m_NextNode)
|
|
{
|
|
m_NextNode->DeleteChildren();
|
|
delete m_NextNode;
|
|
m_NextNode = NULL;
|
|
}
|
|
|
|
if (m_AltNode)
|
|
{
|
|
m_AltNode->DeleteChildren();
|
|
delete m_AltNode;
|
|
m_AltNode = NULL;
|
|
}
|
|
}
|
|
|
|
// CParserTaskType
|
|
// ---------------------------------------------------------------------| Class
|
|
|
|
CParserTaskType::CParserTaskType() : m_BaseNode(NULL)
|
|
{
|
|
}
|
|
|
|
CParserTaskType::~CParserTaskType()
|
|
{
|
|
}
|
|
|
|
// Delete m_BaseNode and all of its children
|
|
void CParserTaskType::DeleteTree()
|
|
{
|
|
if (m_BaseNode)
|
|
{
|
|
m_BaseNode->DeleteChildren();
|
|
delete m_BaseNode;
|
|
m_BaseNode = NULL;
|
|
}
|
|
}
|
|
|
|
// CParserLine
|
|
// ---------------------------------------------------------------------| Class
|
|
|
|
CParserLine::CParserLine()
|
|
{
|
|
}
|
|
|
|
CParserLine::~CParserLine()
|
|
{
|
|
|
|
ClearArguments();
|
|
}
|
|
|
|
// Clear arguments (deleting m_Memory
|
|
bool CParserLine::ClearArguments()
|
|
{
|
|
// Now we can actually clear it
|
|
m_Arguments.clear();
|
|
return true;
|
|
}
|
|
|
|
// Implementation of CParserFile::GetArg*
|
|
// it just checks if argument isn't out of range, and
|
|
// then it uses the the respective function in CParserValue
|
|
FUNC_IMPL_GETARG(GetArgString, GetString, string)
|
|
FUNC_IMPL_GETARG(GetArgBool, GetBool, bool)
|
|
FUNC_IMPL_GETARG(GetArgChar, GetChar, char)
|
|
FUNC_IMPL_GETARG(GetArgShort, GetShort, short)
|
|
FUNC_IMPL_GETARG(GetArgInt, GetInt, int)
|
|
FUNC_IMPL_GETARG(GetArgLong, GetLong, long)
|
|
FUNC_IMPL_GETARG(GetArgUnsignedShort, GetUnsignedShort, unsigned short)
|
|
FUNC_IMPL_GETARG(GetArgUnsignedInt, GetUnsignedInt, unsigned int)
|
|
FUNC_IMPL_GETARG(GetArgUnsignedLong, GetUnsignedLong, unsigned long)
|
|
FUNC_IMPL_GETARG(GetArgFloat, GetFloat, float)
|
|
FUNC_IMPL_GETARG(GetArgDouble, GetDouble, double)
|
|
|
|
// ParseString
|
|
// ------------------------------------------------------------------| Function
|
|
// Parses a line, dividing it into segments according to defined semantics
|
|
// each segment is called an argument and represents a value of some kind
|
|
// ex:
|
|
// variable = 5 => variable, =, 5
|
|
// CallFunc(4,2) => CallFunc, 4, 2
|
|
|
|
// TODO Gee: Make Parser use CStr.
|
|
bool CParserLine::ParseString(const CParser& Parser, const std::string &strLine)
|
|
{
|
|
// Don't process empty string
|
|
if (strLine == string())
|
|
{
|
|
m_ParseOK = false; // Empty lines should never be inputted by CParserFile
|
|
return m_ParseOK;
|
|
}
|
|
|
|
// Locals
|
|
bool Extract=false;
|
|
size_t ExtractPos=0;
|
|
char Buffer[256];
|
|
char Letter[] = {'\0','\0'}; // Letter as string
|
|
vector<string> Segments;
|
|
string strSub;
|
|
size_t i;
|
|
|
|
// Set result to false, then if a match is found, turn it true
|
|
m_ParseOK = false;
|
|
|
|
/*
|
|
TODO Gee Remove this comment!
|
|
// Remove C++-styled comments!
|
|
// * * * *
|
|
int pos = strLine.find("//");
|
|
if (pos != string::npos)
|
|
strLine = strLine.substr(0,pos);
|
|
*/
|
|
|
|
// Divide string into smaller vectors, separators are unusual signs
|
|
// * * * *
|
|
|
|
for (i=0; i<strLine.size(); ++i)
|
|
{
|
|
// Check if we're trying to use some kind of type
|
|
if (!Extract)
|
|
{
|
|
// GET NAME, IDENT, FLOAT
|
|
if (_IsValueChar(strLine[i]))
|
|
{
|
|
Extract = true;
|
|
ExtractPos = i;
|
|
memset((void*)Buffer, '\0', sizeof(char)*256);
|
|
}
|
|
else
|
|
// GET STRING BETWEEN QUOTES
|
|
if (strLine[i] == '\"')
|
|
{
|
|
// Extract a string, search for another "
|
|
size_t pos = strLine.find("\"", i+1);
|
|
|
|
// If matching can't be found,
|
|
// the parsing will fail!
|
|
if (pos == string::npos)
|
|
{
|
|
// TODO Gee - Output in logfile
|
|
m_ParseOK = false;
|
|
return m_ParseOK;
|
|
}
|
|
|
|
// Get substring
|
|
// Add a " to indicate this is a "..." string
|
|
// and can't be used as name
|
|
strSub = "\"" + strLine.substr(i+1, pos-i-1);
|
|
|
|
// Input substring!
|
|
Segments.push_back(strSub);
|
|
|
|
// Now we can't skip everything that we
|
|
// we just read in, update i
|
|
i = pos;
|
|
}
|
|
// GET JUST ONE CHARACTER
|
|
else
|
|
{
|
|
// Input just the one char
|
|
Letter[0] = strLine[i];
|
|
Segments.push_back(Letter);
|
|
continue;
|
|
}
|
|
}
|
|
// Extract whatever
|
|
if (Extract)
|
|
{
|
|
// No type names are longer than 256 characters
|
|
if (i-ExtractPos >= 256)
|
|
{
|
|
Extract=false;
|
|
}
|
|
else
|
|
{
|
|
// Extract string after $ !
|
|
// break whenever we reach a sign that's not A-Z a-z
|
|
if (_IsValueChar(strLine[i]))
|
|
{
|
|
Buffer[i-ExtractPos] = strLine[i];
|
|
}
|
|
else
|
|
{
|
|
// Extraction is finished
|
|
Extract=false;
|
|
|
|
// strLine[i] is now a non-regular character
|
|
// we'll jump back one step so that will
|
|
// be included next loop
|
|
--i;
|
|
}
|
|
|
|
// Check if string is complete
|
|
if (i == strLine.size()-1)
|
|
Extract=false;
|
|
}
|
|
|
|
// If extraction was finished! Input Buffer
|
|
if (Extract == false)
|
|
{
|
|
Segments.push_back( string(Buffer) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to find an appropriate CParserTaskType in parser
|
|
// * * * *
|
|
|
|
// Locals
|
|
size_t Progress; // progress in Segments index
|
|
size_t Lane=0; // Have many alternative routes we are in
|
|
bool Match; // If a task-type match has been found
|
|
// The vector of these three represents the different lanes
|
|
// LastValidProgress[1] takes you back to lane 1 and how
|
|
// the variables was set at that point
|
|
vector<size_t> LastValidProgress; // When diving into a dynamic argument store store
|
|
// the last valid so you can go back to it
|
|
vector<size_t> LastValidArgCount; // If an alternative route turns out to fail, we
|
|
// need to know the amount of arguments on the last
|
|
// valid position, so we can remove them.
|
|
vector<bool> LastValidMatch; // Match at that point
|
|
bool BlockAltNode = false; // If this turns true, the alternative route
|
|
// tested was not a success, and the settings
|
|
// should be set back in order to test the
|
|
// next node instead
|
|
bool LookNoFurther = false; // If this turns true, it means a definite match has been
|
|
// found and no further looking is required
|
|
CParserTaskTypeNode *CurNode=NULL; // Current node on task type
|
|
CParserTaskTypeNode *PrevNode=NULL; // Last node
|
|
UNUSED(PrevNode);
|
|
|
|
// Iterate all different TaskType, and all TaskTypeElements...
|
|
// start from left and go to the right (prog), comparing
|
|
// the similarities. If enough
|
|
// similarities are found, then we can declare progress as
|
|
// that type and exit loop
|
|
vector<CParserTaskType>::const_iterator cit_tt;
|
|
for (cit_tt = Parser.m_TaskTypes.begin();
|
|
cit_tt != Parser.m_TaskTypes.end();
|
|
++cit_tt)
|
|
{
|
|
// Reset for this task-type
|
|
Match = true;
|
|
Progress = 0;
|
|
ClearArguments(); // Previous failed can have filled this
|
|
CurNode = cit_tt->m_BaseNode; // Start at base node
|
|
LookNoFurther = false;
|
|
BlockAltNode = false;
|
|
|
|
// This loop will go through the whole tree until
|
|
// it reaches an empty node
|
|
while (!LookNoFurther)
|
|
{
|
|
// Check if node is valid
|
|
// otherwise try to jump back to parent
|
|
if (CurNode->m_NextNode == NULL &&
|
|
(CurNode->m_AltNode == NULL || BlockAltNode))
|
|
{
|
|
// Jump back to valid
|
|
//CurNode = PrevNode;
|
|
|
|
// If the node has no children, it's the last, and we're
|
|
// on lane 0, i.e. with no
|
|
if (CurNode->m_NextNode == NULL &&
|
|
(CurNode->m_AltNode == NULL || BlockAltNode) &&
|
|
Lane == 0)
|
|
{
|
|
if (Progress != Segments.size())
|
|
Match = false;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CParserTaskTypeNode *OldNode = NULL;
|
|
|
|
// Go back to regular route!
|
|
for(;;)
|
|
{
|
|
OldNode = CurNode;
|
|
CurNode = CurNode->m_ParentNode;
|
|
|
|
if (CurNode->m_AltNode == OldNode)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the alternative route isn't repeatable, block alternative route for
|
|
// next loop cycle
|
|
if (!CurNode->m_AltNodeRepeatable)
|
|
BlockAltNode = true;
|
|
|
|
// Decrease lane
|
|
--Lane;
|
|
}
|
|
}
|
|
|
|
// Check alternative route
|
|
// * * * *
|
|
|
|
// Check if alternative route is present
|
|
// note, if an alternative node has already failed
|
|
// we don't want to force usage of the next node
|
|
// therefore BlockAltNode has to be false
|
|
if (!BlockAltNode)
|
|
{
|
|
if (CurNode->m_AltNode)
|
|
{
|
|
// Alternative route found, we'll test this first!
|
|
CurNode = CurNode->m_AltNode;
|
|
|
|
// --- New node is set!
|
|
|
|
// Make sure they are large enough
|
|
if ((int)LastValidProgress.size() < Lane+1)
|
|
{
|
|
LastValidProgress.resize(Lane+1);
|
|
LastValidMatch.resize(Lane+1);
|
|
LastValidArgCount.resize(Lane+1);
|
|
}
|
|
|
|
// Store last valid progress
|
|
LastValidProgress[Lane] = Progress;
|
|
LastValidMatch[Lane] = Match;
|
|
LastValidArgCount[Lane] = (int)m_Arguments.size();
|
|
|
|
++Lane;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
else BlockAltNode = false;
|
|
|
|
// Now check Regular Next Node
|
|
// * * * *
|
|
|
|
if (CurNode->m_NextNode)
|
|
{
|
|
// Important!
|
|
// Change working node to the next node!
|
|
CurNode = CurNode->m_NextNode;
|
|
|
|
// --- New node is set!
|
|
|
|
// CHECK IF LETTER IS CORRECT
|
|
if (CurNode->m_Letter != '\0')
|
|
{
|
|
// OPTIONALLY SKIP BLANK SPACES
|
|
if (CurNode->m_Letter == '_')
|
|
{
|
|
// Find blank space if any!
|
|
// and jump to the next non-blankspace
|
|
if (Progress < Segments.size())
|
|
{
|
|
// Skip blankspaces AND tabs!
|
|
while (Segments[Progress].size()==1 &&
|
|
(Segments[Progress][0]==' ' ||
|
|
Segments[Progress][0]=='\t'))
|
|
{
|
|
++Progress;
|
|
|
|
// Check length
|
|
if (Progress >= (int)Segments.size())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// CHECK LETTER IF IT'S CORRECT
|
|
{
|
|
if (Progress < Segments.size())
|
|
{
|
|
// This should be 1-Letter long
|
|
if (Segments[Progress].size() != 1)
|
|
Match = false;
|
|
|
|
// Check Letter
|
|
if (CurNode->m_Letter != Segments[Progress][0])
|
|
Match = false;
|
|
|
|
// Update progress
|
|
++Progress;
|
|
}
|
|
else Match = false;
|
|
}
|
|
}
|
|
|
|
else if (CurNode->m_Type == typeNull)
|
|
{
|
|
// Match without doing anything (leaving Match==true)
|
|
}
|
|
|
|
// CHECK NAME
|
|
else
|
|
{
|
|
// Do this first, because we wan't to
|
|
// avoid the Progress and Segments.size()
|
|
// check for this
|
|
if (CurNode->m_Type == typeAddArg)
|
|
{
|
|
// Input argument
|
|
CParserValue value;
|
|
value.m_String = CurNode->m_String;
|
|
m_Arguments.push_back(value);
|
|
}
|
|
else
|
|
{
|
|
// Alright! An ident or const has been acquired, if we
|
|
// can't find any or if the string has run out
|
|
// that invalidates the match
|
|
|
|
// String end?
|
|
if (Progress >= (int)Segments.size())
|
|
{
|
|
Match = false;
|
|
}
|
|
else
|
|
{
|
|
// Store argument in CParserValue!
|
|
CParserValue value;
|
|
size_t i;
|
|
|
|
switch(CurNode->m_Type)
|
|
{
|
|
case typeIdent:
|
|
// Check if this really is a string
|
|
if (!_IsStrictNameChar(Segments[Progress][0]))
|
|
{
|
|
Match = false;
|
|
break;
|
|
}
|
|
|
|
// Same as at typeValue, but this time
|
|
// we won't allow strings like "this", just
|
|
// like this
|
|
if (Segments[Progress][0] == '\"')
|
|
Match = false;
|
|
else
|
|
value.m_String = Segments[Progress];
|
|
|
|
// Input argument!
|
|
m_Arguments.push_back(value);
|
|
|
|
++Progress;
|
|
break;
|
|
case typeValue:
|
|
// Check if this really is a string
|
|
if (!_IsValueChar(Segments[Progress][0]) &&
|
|
Segments[Progress][0] != '\"')
|
|
{
|
|
Match = false;
|
|
break;
|
|
}
|
|
|
|
// Check if initial is -> " <-, because that means it was
|
|
// stored from a "String like these with quotes"
|
|
// We don't want to store that prefix
|
|
if (Segments[Progress][0] == '\"')
|
|
value.m_String = Segments[Progress].substr(1, Segments[Progress].size()-1);
|
|
else
|
|
value.m_String = Segments[Progress];
|
|
|
|
// Input argument!
|
|
m_Arguments.push_back(value);
|
|
|
|
++Progress;
|
|
break;
|
|
case typeRest:
|
|
// Extract the whole of the string
|
|
|
|
// Reset, probably is but still
|
|
value.m_String = string();
|
|
|
|
for (i=Progress; i<Segments.size(); ++i)
|
|
{
|
|
value.m_String += Segments[i];
|
|
|
|
// If argument starts with => " <=, add one to the end of it too
|
|
if (Segments[i][0] == '"')
|
|
value.m_String += "\"";
|
|
}
|
|
|
|
m_Arguments.push_back(value);
|
|
|
|
// Now BREAK EVERYTHING !
|
|
// We're done, we found our match and let's get out
|
|
LookNoFurther = true;
|
|
//Match = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if match is false! if it is, try returning to last valid state
|
|
if (!Match && Lane > 0)
|
|
{
|
|
// The alternative route failed
|
|
BlockAltNode = true;
|
|
|
|
CParserTaskTypeNode *OldNode = NULL;
|
|
|
|
// Go back to regular route!
|
|
for(;;)
|
|
{
|
|
OldNode = CurNode;
|
|
CurNode = CurNode->m_ParentNode;
|
|
|
|
if (CurNode->m_AltNode == OldNode)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Decrease lane
|
|
--Lane;
|
|
|
|
// Restore values as before
|
|
Progress = LastValidProgress[Lane];
|
|
Match = LastValidMatch[Lane];
|
|
m_Arguments.resize(LastValidArgCount[Lane]);
|
|
}
|
|
}
|
|
|
|
// Check if it was a match!
|
|
if (Match)
|
|
{
|
|
// Before we celebrate the match, let's check if whole
|
|
// of Segments has been used, and if so we have to
|
|
// nullify the match
|
|
//if (Progress == Segments.size())
|
|
{
|
|
// *** REPORT MATCH WAS FOUND ***
|
|
m_TaskTypeName = cit_tt->m_Name;
|
|
m_ParseOK = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// POST-PROCESSING OF ARGUMENTS!
|
|
|
|
// if _minus is found as argument, remove it and add "-" to the one after that
|
|
// note, it's easier if std::iterator isn't used here
|
|
|
|
for (i=1; i<(int)GetArgCount(); ++i)
|
|
{
|
|
if (m_Arguments[i-1].m_String == "_minus")
|
|
{
|
|
// Add "-" to next, and remove "_minus"
|
|
m_Arguments[i].m_String = "-" + m_Arguments[i].m_String;
|
|
m_Arguments.erase(m_Arguments.begin() + (i-1));
|
|
}
|
|
}
|
|
|
|
return m_ParseOK;
|
|
}
|
|
|
|
// CParser
|
|
// ---------------------------------------------------------------------| Class
|
|
|
|
// ctor
|
|
CParser::CParser()
|
|
{
|
|
}
|
|
|
|
// dtor
|
|
CParser::~CParser()
|
|
{
|
|
// Delete all task type trees
|
|
vector<CParserTaskType>::iterator itTT;
|
|
for (itTT = m_TaskTypes.begin();
|
|
itTT != m_TaskTypes.end();
|
|
++itTT)
|
|
{
|
|
itTT->DeleteTree();
|
|
}
|
|
}
|
|
|
|
// InputTaskType
|
|
// ------------------------------------------------------------------| Function
|
|
// A task-type is a string representing the acquired syntax when parsing
|
|
// This function converts that string into a binary tree, making it easier
|
|
// and faster to later parse.
|
|
bool CParser::InputTaskType(const string& strName, const string& strSyntax)
|
|
{
|
|
// Locals
|
|
CParserTaskType TaskType; // Object we acquire to create
|
|
char Buffer[REGULAR_MAX_LENGTH];
|
|
size_t ExtractPos = 0;
|
|
bool Extract = false;
|
|
bool Error = false;
|
|
size_t i;
|
|
bool ConstructNew = false; // If it's the first input, then don't
|
|
// construct a new node, because we
|
|
// we already have m_BaseNode
|
|
|
|
// Construct base node
|
|
TaskType.m_BaseNode = new CParserTaskTypeNode();
|
|
|
|
// Working node
|
|
CParserTaskTypeNode *CurNode = TaskType.m_BaseNode;
|
|
|
|
// Loop through the string and construct nodes in the binary tree
|
|
// when applicable
|
|
for (i=0; i<(int)strSyntax.size(); ++i)
|
|
{
|
|
// Extract is a variable that is true when we want to extract
|
|
// parts that is longer than one character.
|
|
if (!Extract)
|
|
{
|
|
if (strSyntax[i] == REGULAR_EXPRESSION)
|
|
{
|
|
Extract = true;
|
|
ExtractPos = i+1; // Skip $
|
|
memset((void*)Buffer, '\0', sizeof(char)*REGULAR_MAX_LENGTH);
|
|
|
|
// We don't want to extract '$' so we'll just continue to next loop run
|
|
continue;
|
|
}
|
|
else
|
|
if (strSyntax[i] == START_DYNAMIC || strSyntax[i] == START_OPTIONAL)
|
|
{
|
|
|
|
// Slight hack: because things are stored in a binary tree,
|
|
// it can't handle "<[a][b]>" -- the <...> node has only
|
|
// one slot for an optional [...] node. To avoid this problem,
|
|
// typeNull nodes are used to indicate things that always
|
|
// succeed but can have altnodes attached:
|
|
/*
|
|
parent parent
|
|
\ ===> \
|
|
<...> ===> <...> <-- CurNode
|
|
/ \ / \
|
|
/ \ / \
|
|
next [a] Null [a] <-- added NewNode
|
|
/ \
|
|
next [b]
|
|
*/
|
|
if (CurNode->m_AltNode)
|
|
{
|
|
|
|
// Rearrange the tree, as shown above:
|
|
|
|
// Create NewNode
|
|
CParserTaskTypeNode* NewNode = new CParserTaskTypeNode();
|
|
NewNode->m_ParentNode = CurNode;
|
|
NewNode->m_Letter = '\0';
|
|
NewNode->m_Type = typeNull;
|
|
|
|
// Copy 'next' into NewNode
|
|
NewNode->m_NextNode = CurNode->m_NextNode;
|
|
// Replace 'next' with NewNode inside CurNode
|
|
CurNode->m_NextNode = NewNode;
|
|
|
|
// Update CurNode, so the following code inserts into [b]
|
|
CurNode = NewNode;
|
|
}
|
|
|
|
// Dive into the alternative node
|
|
assert(! CurNode->m_AltNode);
|
|
CurNode->m_AltNode = new CParserTaskTypeNode();
|
|
CurNode->m_AltNode->m_ParentNode = CurNode;
|
|
|
|
// It's repeatable
|
|
CurNode->m_AltNodeRepeatable = bool(strSyntax[i]==START_DYNAMIC);
|
|
|
|
// Set to current
|
|
CurNode = CurNode->m_AltNode;
|
|
|
|
ConstructNew = false;
|
|
|
|
// We're done extracting for now
|
|
continue;
|
|
}
|
|
else
|
|
if (strSyntax[i] == END_DYNAMIC || strSyntax[i] == END_OPTIONAL)
|
|
{
|
|
CParserTaskTypeNode *OldNode = NULL;
|
|
|
|
// Jump out of this alternative route
|
|
for(;;)
|
|
{
|
|
OldNode = CurNode;
|
|
CurNode = CurNode->m_ParentNode;
|
|
|
|
if (CurNode == NULL)
|
|
{
|
|
// Syntax error
|
|
Error = true;
|
|
break;
|
|
}
|
|
|
|
if (CurNode->m_AltNode == OldNode)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Error)break;
|
|
}
|
|
else
|
|
{
|
|
// Check if this is the first input
|
|
// CONSTRUCT A CHILD NODE
|
|
assert(! CurNode->m_NextNode);
|
|
CurNode->m_NextNode = new CParserTaskTypeNode();
|
|
CurNode->m_NextNode->m_ParentNode = CurNode;
|
|
|
|
// Jump into !
|
|
CurNode = CurNode->m_NextNode;
|
|
|
|
// Set CurNode
|
|
CurNode->m_Letter = strSyntax[i];
|
|
}
|
|
}
|
|
|
|
// Extact
|
|
if (Extract)
|
|
{
|
|
// No type names are longer than REGULAR_MAX_LENGTH characters
|
|
if (i-ExtractPos >= REGULAR_MAX_LENGTH)
|
|
{
|
|
Extract=false;
|
|
}
|
|
else
|
|
{
|
|
// Extract string after $ !
|
|
// break whenever we reach a sign that's not A-Z a-z
|
|
if (_IsStrictNameChar(strSyntax[i]))
|
|
{
|
|
Buffer[i-ExtractPos] = strSyntax[i];
|
|
}
|
|
else
|
|
{
|
|
// Extraction is finished
|
|
Extract=false;
|
|
|
|
// strLine[i] is now a non-regular character
|
|
// we'll jump back one step so that will
|
|
// be included next loop
|
|
--i;
|
|
}
|
|
|
|
// Check if string is complete
|
|
if (i == strSyntax.size()-1)
|
|
Extract=false;
|
|
}
|
|
|
|
// If extraction was finished! Input Buffer
|
|
if (Extract == false)
|
|
{
|
|
// CONSTRUCT A CHILD NODE
|
|
assert(! CurNode->m_NextNode);
|
|
CurNode->m_NextNode = new CParserTaskTypeNode();
|
|
CurNode->m_NextNode->m_ParentNode = CurNode;
|
|
|
|
// Jump into !
|
|
CurNode = CurNode->m_NextNode;
|
|
|
|
CurNode->m_Letter = '\0';
|
|
|
|
string str = string(Buffer);
|
|
|
|
// Check value and set up CurNode accordingly
|
|
if (str == "value") CurNode->m_Type = typeValue;
|
|
else
|
|
if (str == "ident") CurNode->m_Type = typeIdent;
|
|
else
|
|
if (str == "rest") CurNode->m_Type = typeRest;
|
|
else
|
|
if (str == "rbracket") CurNode->m_Letter = '>';
|
|
else
|
|
if (str == "lbracket") CurNode->m_Letter = '<';
|
|
else
|
|
if (str == "rbrace") CurNode->m_Letter = ']';
|
|
else
|
|
if (str == "lbrace") CurNode->m_Letter = '[';
|
|
else
|
|
if (str == "dollar") CurNode->m_Letter = '$';
|
|
else
|
|
if (str == "arg")
|
|
{
|
|
// After $arg, you need a parenthesis, within that parenthesis is a string
|
|
// that will be added as an argument when it's passed through
|
|
|
|
CurNode->m_Type = typeAddArg;
|
|
|
|
// Check length, it has to have place for at least a '(' and ')' after $arg
|
|
if (ExtractPos+4 >= strSyntax.size())
|
|
{
|
|
Error = true;
|
|
break;
|
|
}
|
|
|
|
// We want to extract what's inside the parenthesis after $arg
|
|
// if it's not there at all, it's a syntactical error
|
|
if (strSyntax[ExtractPos+3] != '(')
|
|
{
|
|
Error = true;
|
|
break;
|
|
}
|
|
|
|
// Now try finding the second ')'
|
|
size_t Pos = strSyntax.find(")", ExtractPos+5);
|
|
|
|
// Check if ')' exists at all
|
|
if (Pos == string::npos)
|
|
{
|
|
Error = true;
|
|
break;
|
|
}
|
|
|
|
// Now extract string within ( and )
|
|
CurNode->m_String = strSyntax.substr(ExtractPos+4, Pos-(ExtractPos+4));
|
|
|
|
// Now update position
|
|
i = (int)Pos;
|
|
}
|
|
else
|
|
{
|
|
// TODO Gee report in log too
|
|
Error = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Input TaskType
|
|
if (!Error)
|
|
{
|
|
// Set name and input
|
|
TaskType.m_Name = strName;
|
|
m_TaskTypes.push_back(TaskType);
|
|
}
|
|
|
|
return !Error;
|
|
}
|
|
|
|
|
|
CParserCache::CacheType CParserCache::m_Cached;
|
|
|
|
CParser& CParserCache::Get(const char* str)
|
|
{
|
|
CacheType::iterator it = m_Cached.find(str);
|
|
if (it == m_Cached.end())
|
|
{
|
|
CParser* parser = new CParser;
|
|
parser->InputTaskType("", str);
|
|
m_Cached[str] = parser;
|
|
return *parser;
|
|
}
|
|
else
|
|
{
|
|
CParser* parser = it->second;
|
|
return *parser;
|
|
}
|
|
}
|