1
0
forked from 0ad/0ad
0ad/source/ps/Parser.cpp
janwas 4663ac0fe7 split debug_assert into ENSURE and ASSERT as discussed in a previous meeting.
the old debug_assert always ran and tested the expression, which slows
down release builds. wrapping them in #ifndef NDEBUG is clumsy. the new
ASSERT behaves like assert and ENSURE like the old debug_assert. Let's
change any time-critical but not-super-important ENSURE to ASSERT to
speed up release builds. (already done in bits.h and unique_range.h)

This was SVN commit r9362.
2011-04-30 13:01:45 +00:00

1085 lines
26 KiB
C++

/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "Parser.h"
#if MSC_VERSION
#pragma warning(disable:4786)
#endif
//-------------------------------------------------
// 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(size_t arg, type &ret) \
{ \
if (GetArgCount() <= 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 std::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 == std::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;
}
// std::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, std::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 std::string
if (strLine == std::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 std::string
std::vector<std::string> Segments;
std::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 != std::string::npos)
strLine = strLine.substr(0,pos);
*/
// Divide std::string into smaller std::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 std::string, search for another "
size_t pos = strLine.find("\"", i+1);
// If matching can't be found,
// the parsing will fail!
if (pos == std::string::npos)
{
// TODO Gee - Output in logfile
m_ParseOK = false;
return m_ParseOK;
}
// Get substring
// Add a " to indicate this is a "..." std::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 std::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 std::string is complete
if (i == strLine.size()-1)
Extract=false;
}
// If extraction was finished! Input Buffer
if (Extract == false)
{
Segments.push_back( std::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 std::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
std::vector<size_t> LastValidProgress; // When diving into a dynamic argument store store
// the last valid so you can go back to it
std::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.
std::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
UNUSED2(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
std::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 (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 >= 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 std::string has run out
// that invalidates the match
// String end?
if (Progress >= Segments.size())
{
Match = false;
}
else
{
// Store argument in CParserValue!
CParserValue value;
switch(CurNode->m_Type)
{
case typeIdent:
// Check if this really is a std::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 std::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:
// This is a comment, don't store it.
// 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<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
std::vector<CParserTaskType>::iterator itTT;
for (itTT = m_TaskTypes.begin();
itTT != m_TaskTypes.end();
++itTT)
{
itTT->DeleteTree();
}
}
// InputTaskType
// ------------------------------------------------------------------| Function
// A task-type is a std::string representing the acquired syntax when parsing
// This function converts that std::string into a binary tree, making it easier
// and faster to later parse.
bool CParser::InputTaskType(const std::string& strName, const std::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;
// Construct base node
TaskType.m_BaseNode = new CParserTaskTypeNode();
// Working node
CParserTaskTypeNode *CurNode = TaskType.m_BaseNode;
// Loop through the std::string and construct nodes in the binary tree
// when applicable
for (i=0; i<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
ENSURE(! 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;
// 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
ENSURE(! 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 std::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 std::string is complete
if (i == strSyntax.size()-1)
Extract=false;
}
// If extraction was finished! Input Buffer
if (Extract == false)
{
// CONSTRUCT A CHILD NODE
ENSURE(! CurNode->m_NextNode);
CurNode->m_NextNode = new CParserTaskTypeNode();
CurNode->m_NextNode->m_ParentNode = CurNode;
// Jump into !
CurNode = CurNode->m_NextNode;
CurNode->m_Letter = '\0';
std::string str = std::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 std::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 == std::string::npos)
{
Error = true;
break;
}
// Now extract std::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;
}
}