Added class ParsePoint for keeping a context while parsing code.

Changed the Javascript parser to use this object.
This patch fixes issues with incorrect parsing of imbricated if... else/if {} else {} or any of the combinations including a if else construct.



git-svn-id: http://voip.null.ro/svn/yate@5464 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
oana 2013-04-12 15:07:19 +00:00
parent 089242bfc5
commit 62835337f7
3 changed files with 299 additions and 158 deletions

View File

@ -143,6 +143,17 @@ bool ExpExtender::runAssign(ObjList& stack, const ExpOperation& oper, GenObject*
return false;
}
ParsePoint& ParsePoint::operator=(ParsePoint& parsePoint)
{
m_expr = parsePoint.m_expr;
m_count = parsePoint.m_count;
m_searchedSeps = parsePoint.m_searchedSeps;
m_fileName = parsePoint.m_fileName;
m_lineNo = parsePoint.m_lineNo;
if (m_eval)
m_eval->m_lineNo = m_lineNo;
return *this;
}
ExpEvaluator::ExpEvaluator(const TokenDict* operators, const TokenDict* unaryOps)
: m_operators(operators), m_unaryOps(unaryOps),
@ -199,9 +210,9 @@ void ExpEvaluator::extender(ExpExtender* ext)
TelEngine::destruct(tmp->refObj());
}
char ExpEvaluator::skipWhites(const char*& expr)
char ExpEvaluator::skipWhites(ParsePoint& expr)
{
if (!expr)
if (!expr.m_expr)
return 0;
for (; ; expr++) {
char c = *expr;
@ -210,12 +221,12 @@ char ExpEvaluator::skipWhites(const char*& expr)
case '\t':
continue;
case '\r':
m_lineNo++;
expr.m_lineNo = ++m_lineNo;
if (expr[1] == '\n')
expr++;
continue;
case '\n':
m_lineNo++;
expr.m_lineNo = ++m_lineNo;
if (expr[1] == '\r')
expr++;
continue;
@ -231,12 +242,12 @@ bool ExpEvaluator::keywordChar(char c) const
('0' <= c && c <= '9') || (c == '_');
}
char ExpEvaluator::skipComments(const char*& expr, GenObject* context)
char ExpEvaluator::skipComments(ParsePoint& expr, GenObject* context)
{
return skipWhites(expr);
}
int ExpEvaluator::preProcess(const char*& expr, GenObject* context)
int ExpEvaluator::preProcess(ParsePoint& expr, GenObject* context)
{
return -1;
}
@ -290,16 +301,16 @@ void ExpEvaluator::formatLineNo(String& buf, unsigned int line) const
buf << "line " << line;
}
bool ExpEvaluator::getInstruction(const char*& expr, char stop, GenObject* nested)
bool ExpEvaluator::getInstruction(ParsePoint& expr, char stop, GenObject* nested)
{
return false;
}
bool ExpEvaluator::getOperand(const char*& expr, bool endOk, int precedence)
bool ExpEvaluator::getOperand(ParsePoint& expr, bool endOk, int precedence)
{
if (inError())
return false;
XDebug(this,DebugAll,"getOperand '%.30s'",expr);
XDebug(this,DebugAll,"getOperand '%.30s'",(const char*)expr);
if (!getOperandInternal(expr, endOk, precedence))
return false;
Opcode oper;
@ -308,7 +319,7 @@ bool ExpEvaluator::getOperand(const char*& expr, bool endOk, int precedence)
return true;
}
bool ExpEvaluator::getOperandInternal(const char*& expr, bool endOk, int precedence)
bool ExpEvaluator::getOperandInternal(ParsePoint& expr, bool endOk, int precedence)
{
char c = skipComments(expr);
if (!c)
@ -335,16 +346,16 @@ bool ExpEvaluator::getOperandInternal(const char*& expr, bool endOk, int precede
return gotError("Expecting operand",expr);
}
bool ExpEvaluator::getSimple(const char*& expr, bool constOnly)
bool ExpEvaluator::getSimple(ParsePoint& expr, bool constOnly)
{
return getString(expr) || getNumber(expr);
}
bool ExpEvaluator::getNumber(const char*& expr)
bool ExpEvaluator::getNumber(ParsePoint& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getNumber '%.30s'",expr);
XDebug(this,DebugAll,"getNumber '%.30s'",(const char*)expr);
char* endp = 0;
long int val = ::strtol(expr,&endp,0);
if (!endp || (endp == expr))
@ -355,11 +366,11 @@ bool ExpEvaluator::getNumber(const char*& expr)
return true;
}
bool ExpEvaluator::getString(const char*& expr)
bool ExpEvaluator::getString(ParsePoint& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getString '%.30s'",expr);
XDebug(this,DebugAll,"getString '%.30s'",(const char*)expr);
char c = skipComments(expr);
if (c == '"' || c == '\'') {
String str;
@ -434,18 +445,18 @@ int ExpEvaluator::getKeyword(const char* str) const
return len;
}
bool ExpEvaluator::getFunction(const char*& expr)
bool ExpEvaluator::getFunction(ParsePoint& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getFunction '%.30s'",expr);
XDebug(this,DebugAll,"getFunction '%.30s'",(const char*)expr);
skipComments(expr);
int len = getKeyword(expr);
const char* s = expr+len;
unsigned int savedLine = m_lineNo;
ParsePoint s = expr;
s.m_expr = s.m_expr+len;
skipComments(expr);
if ((len <= 0) || (skipComments(s) != '(')) {
m_lineNo = savedLine;
expr.m_lineNo = s.m_lineNo;
return false;
}
s++;
@ -455,7 +466,7 @@ bool ExpEvaluator::getFunction(const char*& expr)
if (!runCompile(s,')')) {
if (!argc && (skipComments(s) == ')'))
break;
m_lineNo = savedLine;
expr.m_lineNo = s.m_lineNo;
return false;
}
argc++;
@ -463,17 +474,17 @@ bool ExpEvaluator::getFunction(const char*& expr)
if (skipComments(s) != ')')
return gotError("Expecting ')' after function",s);
String str(expr,len);
expr = s+1;
expr.m_expr = s.m_expr+1;
DDebug(this,DebugAll,"Found %s()",str.safe());
addOpcode(OpcFunc,str,argc);
return true;
}
bool ExpEvaluator::getField(const char*& expr)
bool ExpEvaluator::getField(ParsePoint& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"getField '%.30s'",expr);
XDebug(this,DebugAll,"getField '%.30s'",(const char*)expr);
skipComments(expr);
int len = getKeyword(expr);
if (len <= 0)
@ -487,19 +498,19 @@ bool ExpEvaluator::getField(const char*& expr)
return true;
}
ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr)
ExpEvaluator::Opcode ExpEvaluator::getOperator(ParsePoint& expr)
{
skipComments(expr);
return getOperator(expr,m_operators);
}
ExpEvaluator::Opcode ExpEvaluator::getUnaryOperator(const char*& expr)
ExpEvaluator::Opcode ExpEvaluator::getUnaryOperator(ParsePoint& expr)
{
skipComments(expr);
return getOperator(expr,m_unaryOps);
}
ExpEvaluator::Opcode ExpEvaluator::getPostfixOperator(const char*& expr, int priority)
ExpEvaluator::Opcode ExpEvaluator::getPostfixOperator(ParsePoint& expr, int priority)
{
return OpcNone;
}
@ -573,7 +584,7 @@ bool ExpEvaluator::getRightAssoc(ExpEvaluator::Opcode oper) const
}
}
bool ExpEvaluator::getSeparator(const char*& expr, bool remove)
bool ExpEvaluator::getSeparator(ParsePoint& expr, bool remove)
{
if (skipComments(expr) != ',')
return false;
@ -582,7 +593,7 @@ bool ExpEvaluator::getSeparator(const char*& expr, bool remove)
return true;
}
bool ExpEvaluator::runCompile(const char*& expr, char stop, GenObject* nested)
bool ExpEvaluator::runCompile(ParsePoint& expr, char stop, GenObject* nested)
{
char buf[2];
const char* stopStr = 0;
@ -594,7 +605,7 @@ bool ExpEvaluator::runCompile(const char*& expr, char stop, GenObject* nested)
return runCompile(expr,stopStr,nested);
}
bool ExpEvaluator::runCompile(const char*& expr, const char* stop, GenObject* nested)
bool ExpEvaluator::runCompile(ParsePoint& expr, const char* stop, GenObject* nested)
{
typedef struct {
Opcode code;
@ -603,7 +614,7 @@ bool ExpEvaluator::runCompile(const char*& expr, const char* stop, GenObject* ne
StackedOpcode stack[10];
unsigned int stackPos = 0;
#ifdef DEBUG
Debugger debug(DebugInfo,"runCompile()"," '%s' %p '%.30s'",TelEngine::c_safe(stop),nested,expr);
Debugger debug(DebugInfo,"runCompile()"," '%s' %p '%.30s'",TelEngine::c_safe(stop),nested,(const char*)expr);
#endif
if (skipComments(expr) == ')')
return false;
@ -616,12 +627,15 @@ bool ExpEvaluator::runCompile(const char*& expr, const char* stop, GenObject* ne
char stopChar = stop ? stop[0] : '\0';
for (;;) {
while (!stackPos && skipComments(expr) && (!stop || !::strchr(stop,*expr)) && getInstruction(expr,stopChar,nested))
;
if (expr.m_searchedSeps && expr.m_foundSep && ::strchr(expr.m_searchedSeps,expr.m_foundSep))
return true;
if (inError())
return false;
char c = skipComments(expr);
if (c && stop && ::strchr(stop,c))
if (c && stop && ::strchr(stop,c)) {
expr.m_foundSep = c;
return true;
}
if (!getOperand(expr))
return false;
Opcode oper;
@ -1358,8 +1372,10 @@ bool ExpEvaluator::runAllFields(ObjList& stack, GenObject* context) const
return ok;
}
int ExpEvaluator::compile(const char* expr, GenObject* context)
int ExpEvaluator::compile(ParsePoint& expr, GenObject* context)
{
if (!expr.m_eval)
expr.m_eval = this;
if (!skipComments(expr,context))
return 0;
int res = 0;

View File

@ -157,8 +157,8 @@ public:
bool link();
inline bool traceable() const
{ return m_traceable; }
JsObject* parseArray(const char*& expr, bool constOnly);
JsObject* parseObject(const char*& expr, bool constOnly);
JsObject* parseArray(ParsePoint& expr, bool constOnly);
JsObject* parseObject(ParsePoint& expr, bool constOnly);
inline const NamedList& pragmas() const
{ return m_pragmas; }
inline static unsigned int getLineNo(unsigned int line)
@ -175,20 +175,20 @@ protected:
{ m_traceable = allowed; }
void setBaseFile(const String& file);
virtual void formatLineNo(String& buf, unsigned int line) const;
virtual bool getString(const char*& expr);
virtual bool getString(ParsePoint& expr);
virtual bool getEscape(const char*& expr, String& str, char sep);
virtual bool keywordChar(char c) const;
virtual int getKeyword(const char* str) const;
virtual char skipComments(const char*& expr, GenObject* context = 0);
virtual int preProcess(const char*& expr, GenObject* context = 0);
virtual bool getInstruction(const char*& expr, char stop, GenObject* nested);
virtual bool getSimple(const char*& expr, bool constOnly = false);
virtual Opcode getOperator(const char*& expr);
virtual Opcode getUnaryOperator(const char*& expr);
virtual Opcode getPostfixOperator(const char*& expr, int precedence);
virtual char skipComments(ParsePoint& expr, GenObject* context = 0);
virtual int preProcess(ParsePoint& expr, GenObject* context = 0);
virtual bool getInstruction(ParsePoint& expr, char stop, GenObject* nested);
virtual bool getSimple(ParsePoint& expr, bool constOnly = false);
virtual Opcode getOperator(ParsePoint& expr);
virtual Opcode getUnaryOperator(ParsePoint& expr);
virtual Opcode getPostfixOperator(ParsePoint& expr, int precedence);
virtual const char* getOperator(Opcode oper) const;
virtual int getPrecedence(ExpEvaluator::Opcode oper) const;
virtual bool getSeparator(const char*& expr, bool remove);
virtual bool getSeparator(ParsePoint& expr, bool remove);
virtual bool runOperation(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
virtual bool runFunction(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
virtual bool runField(ObjList& stack, const ExpOperation& oper, GenObject* context) const;
@ -198,17 +198,17 @@ private:
ObjList m_included;
ObjList m_globals;
NamedList m_pragmas;
bool preProcessInclude(const char*& expr, bool once, GenObject* context);
bool preProcessPragma(const char*& expr, GenObject* context);
bool getOneInstruction(const char*& expr, GenObject* nested);
bool parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested);
bool parseIf(const char*& expr, GenObject* nested);
bool parseSwitch(const char*& expr, GenObject* nested);
bool parseFor(const char*& expr, GenObject* nested);
bool parseWhile(const char*& expr, GenObject* nested);
bool parseVar(const char*& expr);
bool parseTry(const char*& expr, GenObject* nested);
bool parseFuncDef(const char*& expr, bool publish);
bool preProcessInclude(ParsePoint& expr, bool once, GenObject* context);
bool preProcessPragma(ParsePoint& expr, GenObject* context);
bool getOneInstruction(ParsePoint& expr, GenObject* nested);
bool parseInner(ParsePoint& expr, JsOpcode opcode, ParseNested* nested);
bool parseIf(ParsePoint& expr, GenObject* nested);
bool parseSwitch(ParsePoint& expr, GenObject* nested);
bool parseFor(ParsePoint& expr, GenObject* nested);
bool parseWhile(ParsePoint& expr, GenObject* nested);
bool parseVar(ParsePoint& expr);
bool parseTry(ParsePoint& expr, GenObject* nested);
bool parseFuncDef(ParsePoint& expr, bool publish);
bool evalList(ObjList& stack, GenObject* context) const;
bool evalVector(ObjList& stack, GenObject* context) const;
bool jumpToLabel(long int label, GenObject* context) const;
@ -391,13 +391,13 @@ public:
{ return nested ? static_cast<ParseNested*>(nested)->find(opcode) : 0; }
inline static ParseNested* findMatch(GenObject* nested, JsCode::JsOpcode opcode)
{ return nested ? static_cast<ParseNested*>(nested)->findMatch(opcode) : 0; }
static bool parseInner(GenObject* nested, JsCode::JsOpcode opcode, const char*& expr)
static bool parseInner(GenObject* nested, JsCode::JsOpcode opcode, ParsePoint& expr)
{ ParseNested* inner = findMatch(nested,opcode);
return inner && inner->parseInner(expr,opcode); }
protected:
virtual bool isMatch(JsCode::JsOpcode opcode)
{ return false; }
inline bool parseInner(const char*& expr, JsCode::JsOpcode opcode)
inline bool parseInner(ParsePoint& expr, JsCode::JsOpcode opcode)
{ return m_code->parseInner(expr,opcode,this); }
inline ParseNested* find(JsCode::JsOpcode opcode)
{ return (opcode == m_opcode) ? this :
@ -967,7 +967,7 @@ void JsCode::formatLineNo(String& buf, unsigned int line) const
buf << (file ? file->toString().c_str() : "???") << ":" << (line & 0xffffff);
}
bool JsCode::getString(const char*& expr)
bool JsCode::getString(ParsePoint& expr)
{
if (inError())
return false;
@ -1102,7 +1102,7 @@ int JsCode::getKeyword(const char* str) const
return len;
}
char JsCode::skipComments(const char*& expr, GenObject* context)
char JsCode::skipComments(ParsePoint& expr, GenObject* context)
{
char c = skipWhites(expr);
while (c == '/') {
@ -1138,7 +1138,7 @@ void JsCode::setBaseFile(const String& file)
m_lineNo = ((idx + 1) << 24) | 1;
}
bool JsCode::preProcessInclude(const char*& expr, bool once, GenObject* context)
bool JsCode::preProcessInclude(ParsePoint& expr, bool once, GenObject* context)
{
if (m_depth > 5)
return gotError("Possible recursive include");
@ -1162,12 +1162,12 @@ bool JsCode::preProcessInclude(const char*& expr, bool once, GenObject* context)
idx = m_included.index(s);
}
// use the upper bits of line # for file index
unsigned int savedLine = m_lineNo;
m_lineNo = ((idx + 1) << 24) | 1;
unsigned int savedLine = expr.m_lineNo;
expr.m_lineNo = m_lineNo = ((idx + 1) << 24) | 1;
m_depth++;
ok = parser->parseFile(str,true);
m_depth--;
m_lineNo = savedLine;
expr.m_lineNo = m_lineNo = savedLine;
}
}
return ok || gotError("Failed to include " + str);
@ -1177,13 +1177,14 @@ bool JsCode::preProcessInclude(const char*& expr, bool once, GenObject* context)
return gotError("Expecting include file",expr);
}
bool JsCode::preProcessPragma(const char*& expr, GenObject* context)
bool JsCode::preProcessPragma(ParsePoint& expr, GenObject* context)
{
skipComments(expr);
int len = ExpEvaluator::getKeyword(expr);
if (len <= 0)
return gotError("Expecting pragma code",expr);
const char* str = expr + len;
ParsePoint str = expr;
str += len;
char c = skipComments(str);
if (c == '"' || c == '\'') {
String val;
@ -1197,7 +1198,7 @@ bool JsCode::preProcessPragma(const char*& expr, GenObject* context)
return gotError("Expecting pragma string",expr);
}
int JsCode::preProcess(const char*& expr, GenObject* context)
int JsCode::preProcess(ParsePoint& expr, GenObject* context)
{
int rval = -1;
for (;;) {
@ -1225,49 +1226,67 @@ int JsCode::preProcess(const char*& expr, GenObject* context)
}
}
bool JsCode::getOneInstruction(const char*& expr, GenObject* nested)
bool JsCode::getOneInstruction(ParsePoint& expr, GenObject* nested)
{
if (inError())
return false;
XDebug(this,DebugAll,"JsCode::getOneInstruction %p '%.30s'",nested,expr);
XDebug(this,DebugAll,"JsCode::getOneInstruction %p '%.30s'",nested,(const char*)expr);
const char* savedSeps = expr.m_searchedSeps;
if (skipComments(expr) == '{') {
expr.m_searchedSeps = "}";
if (!getInstruction(expr,0,nested))
return false;
}
else if (!runCompile(expr,";}",nested))
return false;
else {
expr.m_searchedSeps = ";}";
if (!runCompile(expr,";}",nested))
return false;
if (skipComments(expr) == ';') {
expr.m_foundSep = ';';
expr++;
}
}
expr.m_searchedSeps = savedSeps;
if (!expr.m_searchedSeps || expr.m_count)
expr.m_foundSep = 0;
return true;
}
bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
bool JsCode::getInstruction(ParsePoint& expr, char stop, GenObject* nested)
{
if (inError())
return false;
XDebug(this,DebugAll,"JsCode::getInstruction %p '%.1s' '%.30s'",nested,&stop,expr);
XDebug(this,DebugAll,"JsCode::getInstruction %p '%.1s' 'separators=%s' 'count=%u' '%.30s'",nested,&stop,expr.m_searchedSeps,
expr.m_count,(const char*)expr);
if (skipComments(expr) == '{') {
if (stop == ')')
return false;
expr++;
expr.m_count++;
for (;;) {
if (!runCompile(expr,'}',nested))
return false;
bool sep = false;
while (skipComments(expr) && getSeparator(expr,true))
sep = true;
if (*expr == '}' || !sep)
if (*expr.m_expr == '}' || !sep)
break;
}
if (*expr != '}')
return gotError("Expecting '}'",expr);
expr++;
expr.m_foundSep = '}';
if (expr.m_count > 0)
expr.m_count--;
return true;
}
else if (*expr == ';') {
expr++;
expr.m_foundSep = ';';
return true;
}
const char* saved = expr;
unsigned int savedLine = m_lineNo;
expr.m_foundSep = 0;
ParsePoint saved = expr;
Opcode op = ExpEvaluator::getOperator(expr,s_instr);
switch ((JsOpcode)op) {
case (JsOpcode)OpcNone:
@ -1294,7 +1313,6 @@ bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
return parseIf(expr,nested);
case OpcElse:
expr = saved;
m_lineNo = savedLine;
return false;
case OpcSwitch:
return parseSwitch(expr,nested);
@ -1304,7 +1322,7 @@ bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
return parseWhile(expr,nested);
case OpcCase:
if (!ParseNested::parseInner(nested,OpcCase,expr)) {
m_lineNo = savedLine;
expr.m_lineNo = saved.m_lineNo;
return gotError("case not inside switch",saved);
}
if (skipComments(expr) != ':')
@ -1313,7 +1331,7 @@ bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
break;
case OpcDefault:
if (!ParseNested::parseInner(nested,OpcDefault,expr)) {
m_lineNo = savedLine;
expr.m_lineNo = saved.m_lineNo;
return gotError("Unexpected default instruction",saved);
}
if (skipComments(expr) != ':')
@ -1322,7 +1340,7 @@ bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
break;
case OpcBreak:
if (!ParseNested::parseInner(nested,OpcBreak,expr)) {
m_lineNo = savedLine;
expr.m_lineNo = saved.m_lineNo;
return gotError("Unexpected break instruction",saved);
}
if (skipComments(expr) != ';')
@ -1330,7 +1348,7 @@ bool JsCode::getInstruction(const char*& expr, char stop, GenObject* nested)
break;
case OpcCont:
if (!ParseNested::parseInner(nested,OpcCont,expr)) {
m_lineNo = savedLine;
expr.m_lineNo = saved.m_lineNo;
return gotError("Unexpected continue instruction",saved);
}
if (skipComments(expr) != ';')
@ -1391,7 +1409,7 @@ private:
};
// Parse keywords inner to specific instructions
bool JsCode::parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested)
bool JsCode::parseInner(ParsePoint& expr, JsOpcode opcode, ParseNested* nested)
{
switch (*nested) {
case OpcFor:
@ -1400,11 +1418,11 @@ bool JsCode::parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested)
ParseLoop* block = static_cast<ParseLoop*>(nested);
switch (opcode) {
case OpcBreak:
XDebug(this,DebugAll,"Parsing loop:break '%.30s'",expr);
XDebug(this,DebugAll,"Parsing loop:break '%.30s'",(const char*)expr);
addOpcode((Opcode)OpcJump,block->m_lblBreak);
break;
case OpcCont:
XDebug(this,DebugAll,"Parsing loop:continue '%.30s'",expr);
XDebug(this,DebugAll,"Parsing loop:continue '%.30s'",(const char*)expr);
addOpcode((Opcode)OpcJump,block->m_lblCont);
break;
default:
@ -1421,7 +1439,7 @@ bool JsCode::parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested)
return gotError("Encountered case after default",expr);
if (!getSimple(expr,true))
return gotError("Expecting case constant",expr);
XDebug(this,DebugAll,"Parsing switch:case: '%.30s'",expr);
XDebug(this,DebugAll,"Parsing switch:case: '%.30s'",(const char*)expr);
block->m_state = ParseSwitch::InCase;
block->m_cases.append(popOpcode());
addOpcode(OpcLabel,++m_label);
@ -1430,13 +1448,13 @@ bool JsCode::parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested)
case OpcDefault:
if (block->state() == ParseSwitch::InDefault)
return gotError("Duplicate default case",expr);
XDebug(this,DebugAll,"Parsing switch:default: '%.30s'",expr);
XDebug(this,DebugAll,"Parsing switch:default: '%.30s'",(const char*)expr);
block->m_state = ParseSwitch::InDefault;
block->m_lblDefault = ++m_label;
addOpcode(OpcLabel,block->m_lblDefault);
break;
case OpcBreak:
XDebug(this,DebugAll,"Parsing switch:break '%.30s'",expr);
XDebug(this,DebugAll,"Parsing switch:break '%.30s'",(const char*)expr);
addOpcode((Opcode)OpcJump,static_cast<ParseSwitch*>(nested)->m_lblBreak);
break;
default:
@ -1450,8 +1468,9 @@ bool JsCode::parseInner(const char*& expr, JsOpcode opcode, ParseNested* nested)
return true;
}
bool JsCode::parseIf(const char*& expr, GenObject* nested)
bool JsCode::parseIf(ParsePoint& expr, GenObject* nested)
{
XDebug(this,DebugAll,"JsCode::parseIf() '%.30s'",(const char*)expr);
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
if (!runCompile(++expr,')'))
@ -1463,10 +1482,7 @@ bool JsCode::parseIf(const char*& expr, GenObject* nested)
if (!getOneInstruction(expr,nested))
return false;
skipComments(expr);
const char* save = expr;
unsigned int savedLine = m_lineNo;
if (*expr == ';')
skipComments(++expr);
ParsePoint save = expr;
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcElse) {
ExpOperation* jump = addOpcode((Opcode)OpcJump,++m_label);
addOpcode(OpcLabel,cond->number());
@ -1476,13 +1492,12 @@ bool JsCode::parseIf(const char*& expr, GenObject* nested)
}
else {
expr = save;
m_lineNo = savedLine;
addOpcode(OpcLabel,cond->number());
}
return true;
}
bool JsCode::parseSwitch(const char*& expr, GenObject* nested)
bool JsCode::parseSwitch(ParsePoint& expr, GenObject* nested)
{
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
@ -1494,6 +1509,8 @@ bool JsCode::parseSwitch(const char*& expr, GenObject* nested)
if (skipComments(++expr) != '{')
return gotError("Expecting '{'",expr);
expr++;
const char* savedSeps = expr.m_searchedSeps;
expr.m_searchedSeps = "";
ExpOperation* jump = addOpcode((Opcode)OpcJump,++m_label);
ParseSwitch parseStack(this,nested,++m_label);
for (;;) {
@ -1508,6 +1525,9 @@ bool JsCode::parseSwitch(const char*& expr, GenObject* nested)
if (*expr != '}')
return gotError("Expecting '}'",expr);
expr++;
expr.m_searchedSeps = savedSeps;
if (!expr.m_searchedSeps || expr.m_count)
expr.m_foundSep = 0;
// implicit break at end
addOpcode((Opcode)OpcJump,parseStack.m_lblBreak);
addOpcode(OpcLabel,jump->number());
@ -1528,7 +1548,7 @@ bool JsCode::parseSwitch(const char*& expr, GenObject* nested)
return true;
}
bool JsCode::parseFor(const char*& expr, GenObject* nested)
bool JsCode::parseFor(ParsePoint& expr, GenObject* nested)
{
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
@ -1585,7 +1605,7 @@ bool JsCode::parseFor(const char*& expr, GenObject* nested)
return true;
}
bool JsCode::parseWhile(const char*& expr, GenObject* nested)
bool JsCode::parseWhile(ParsePoint& expr, GenObject* nested)
{
if (skipComments(expr) != '(')
return gotError("Expecting '('",expr);
@ -1607,11 +1627,11 @@ bool JsCode::parseWhile(const char*& expr, GenObject* nested)
return true;
}
bool JsCode::parseVar(const char*& expr)
bool JsCode::parseVar(ParsePoint& expr)
{
if (inError())
return false;
XDebug(this,DebugAll,"parseVar '%.30s'",expr);
XDebug(this,DebugAll,"parseVar '%.30s'",(const char*)expr);
skipComments(expr);
int len = ExpEvaluator::getKeyword(expr);
if (len <= 0 || expr[len] == '(')
@ -1624,7 +1644,7 @@ bool JsCode::parseVar(const char*& expr)
return true;
}
bool JsCode::parseTry(const char*& expr, GenObject* nested)
bool JsCode::parseTry(ParsePoint& expr, GenObject* nested)
{
addOpcode((Opcode)OpcTry);
ParseNested parseStack(this,nested,OpcTry);
@ -1649,9 +1669,9 @@ bool JsCode::parseTry(const char*& expr, GenObject* nested)
return true;
}
bool JsCode::parseFuncDef(const char*& expr, bool publish)
bool JsCode::parseFuncDef(ParsePoint& expr, bool publish)
{
XDebug(this,DebugAll,"JsCode::parseFuncDef '%.30s'",expr);
XDebug(this,DebugAll,"JsCode::parseFuncDef '%.30s'",(const char*)expr);
skipComments(expr);
int len = getKeyword(expr);
String name;
@ -1700,11 +1720,11 @@ bool JsCode::parseFuncDef(const char*& expr, bool publish)
return true;
}
ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
ExpEvaluator::Opcode JsCode::getOperator(ParsePoint& expr)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getOperator '%.30s'",expr);
XDebug(this,DebugAll,"JsCode::getOperator '%.30s'",(const char*)expr);
skipComments(expr);
Opcode op = ExpEvaluator::getOperator(expr,s_operators);
if (OpcNone != op)
@ -1712,11 +1732,11 @@ ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
return ExpEvaluator::getOperator(expr);
}
ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
ExpEvaluator::Opcode JsCode::getUnaryOperator(ParsePoint& expr)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getUnaryOperator '%.30s'",expr);
XDebug(this,DebugAll,"JsCode::getUnaryOperator '%.30s'",(const char*)expr);
skipComments(expr);
Opcode op = ExpEvaluator::getOperator(expr,s_unaryOps);
if (OpcNone != op)
@ -1724,11 +1744,11 @@ ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
return ExpEvaluator::getUnaryOperator(expr);
}
ExpEvaluator::Opcode JsCode::getPostfixOperator(const char*& expr, int precedence)
ExpEvaluator::Opcode JsCode::getPostfixOperator(ParsePoint& expr, int precedence)
{
if (inError())
return OpcNone;
XDebug(this,DebugAll,"JsCode::getPostfixOperator '%.30s'",expr);
XDebug(this,DebugAll,"JsCode::getPostfixOperator '%.30s'",(const char*)expr);
if (skipComments(expr) == '[') {
// The Indexing operator has maximum priority!
// No need to check it.
@ -1742,14 +1762,12 @@ ExpEvaluator::Opcode JsCode::getPostfixOperator(const char*& expr, int precedenc
return (Opcode)OpcIndex;
}
skipComments(expr);
const char* save = expr;
unsigned int savedLine = m_lineNo;
ParsePoint save = expr;
Opcode op = ExpEvaluator::getOperator(expr,s_postfixOps);
if (OpcNone != op) {
if (getPrecedence(op) >= precedence)
return op;
expr = save;
m_lineNo = savedLine;
return OpcNone;
}
return ExpEvaluator::getPostfixOperator(expr,precedence);
@ -1795,13 +1813,14 @@ int JsCode::getPrecedence(ExpEvaluator::Opcode oper) const
}
}
bool JsCode::getSeparator(const char*& expr, bool remove)
bool JsCode::getSeparator(ParsePoint& expr, bool remove)
{
if (inError())
return false;
switch (skipComments(expr)) {
case ']':
case ';':
expr.m_foundSep =';';
case ']':
if (remove)
expr++;
return true;
@ -1809,14 +1828,13 @@ bool JsCode::getSeparator(const char*& expr, bool remove)
return ExpEvaluator::getSeparator(expr,remove);
}
bool JsCode::getSimple(const char*& expr, bool constOnly)
bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
{
if (inError())
return false;
XDebug(this,DebugAll,"JsCode::getSimple(%s) '%.30s'",String::boolText(constOnly),expr);
XDebug(this,DebugAll,"JsCode::getSimple(%s) '%.30s'",String::boolText(constOnly),(const char*)expr);
skipComments(expr);
const char* save = expr;
unsigned int savedLine = m_lineNo;
ParsePoint save = expr;
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_constants)) {
case OpcFalse:
addOpcode(false);
@ -1833,7 +1851,6 @@ bool JsCode::getSimple(const char*& expr, bool constOnly)
case OpcFuncDef:
if (constOnly) {
expr = save;
m_lineNo = savedLine;
return false;
}
return parseFuncDef(expr,false);
@ -1850,7 +1867,7 @@ bool JsCode::getSimple(const char*& expr, bool constOnly)
}
// Parse an inline Javascript Array: [ item1, item2, ... ]
JsObject* JsCode::parseArray(const char*& expr, bool constOnly)
JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
{
if (skipComments(expr) != '[')
return 0;
@ -1883,7 +1900,7 @@ JsObject* JsCode::parseArray(const char*& expr, bool constOnly)
// Parse an inline Javascript Object: { prop1: value1, "prop 2": value2, ... }
JsObject* JsCode::parseObject(const char*& expr, bool constOnly)
JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly)
{
if (skipComments(expr) != '{')
return 0;
@ -3267,14 +3284,18 @@ bool JsParser::parse(const char* text, bool fragment, const char* file)
if (TelEngine::null(text))
return false;
String::stripBOM(text);
ParsePoint expr(text,0,0,file);
if (fragment)
return code() && static_cast<JsCode*>(code())->compile(text,this);
return code() && static_cast<JsCode*>(code())->compile(expr,this);
JsCode* code = new JsCode;
setCode(code);
code->deref();
if (!TelEngine::null(file))
expr.m_eval = code;
if (!TelEngine::null(file)) {
code->setBaseFile(file);
if (!code->compile(text,this)) {
expr.m_fileName = file;
}
if (!code->compile(expr,this)) {
setCode(0);
return false;
}
@ -3307,7 +3328,8 @@ ScriptRun::Status JsParser::eval(const String& text, ExpOperation** result, Scri
JsObject* JsParser::parseJSON(const char* text)
{
JsCode* code = new JsCode;
JsObject* jso = code->parseObject(text,true);
ParsePoint pp(text,code);
JsObject* jso = code->parseObject(pp,true);
TelEngine::destruct(code);
return jso;
}

View File

@ -116,12 +116,115 @@ public:
virtual bool runAssign(ObjList& stack, const ExpOperation& oper, GenObject* context);
};
/**
* A class used to keep a parsing context
* @short The parsing context
*/
class YSCRIPT_API ParsePoint
{
public:
/**
* Constructor
* @param expr Expression to be parsed
* @param eval ExpEvaluator associated with this parsing context
* @param lineNo The line number that is currently parsed
* @param fileName File name associated with this context
* @param seps Searched separator during parsing
*/
explicit inline ParsePoint(const char*& expr, ExpEvaluator* eval = 0, unsigned int lineNo = 0,
const char* fileName = 0, const char* seps = 0)
: m_expr(expr), m_searchedSeps(seps), m_count(0), m_foundSep(0), m_lineNo(lineNo), m_eval(eval), m_fileName(fileName)
{ }
/**
* Assignment operator
* @param parsePoint Parsing context which is to be assigned to this parsing context
*/
ParsePoint& operator=(ParsePoint& parsePoint);
/**
* Cast operator to const char*&
*/
inline operator const char*&()
{ return m_expr; }
/**
* Assignement from const char*
*/
inline ParsePoint& operator=(const char* newExpr)
{
m_expr = newExpr;
return *this;
}
/**
* Prefix incrementation operator. Incrementes the internal expression
*/
inline ParsePoint& operator++() // prefix
{
++m_expr;
return *this;
}
/**
* Postfix incrementation operator. Incrementes the internal expression
*/
inline ParsePoint& operator++(int unused) // postfix
{
++m_expr;
return *this;
}
/**
* Get first char in the parsed expression
* @return First char in the expression to be parsed
*/
inline char firstChar()
{ return *m_expr; }
/**
* Expression to be parsed
*/
const char* m_expr;
/**
* Searched instruction separators
*/
const char* m_searchedSeps;
/**
* Number of how many times the parser must encouter a separator
*/
unsigned int m_count;
/**
* Separator that the parser encountered
*/
char m_foundSep;
/**
* Line numbet at which parsing is taking place
*/
unsigned int m_lineNo;
/**
* ExpEvaluator associated with this parsing context
*/
ExpEvaluator* m_eval;
/**
* File name associated for this context
*/
String m_fileName;
};
/**
* A class used to build stack based (posifix) expression parsers and evaluators
* @short An expression parser and evaluator
*/
class YSCRIPT_API ExpEvaluator : public DebugEnabler
{
friend class ParsePoint;
public:
/**
* Parsing styles
@ -229,11 +332,11 @@ public:
/**
* Parse and compile an expression
* @param expr Pointer to expression to compile
* @param expr Parsing context to compile
* @param context Pointer to arbitrary object to be passed to called methods
* @return Number of expressions compiled, zero on error
*/
int compile(const char* expr, GenObject* context = 0);
int compile(ParsePoint& expr, GenObject* context = 0);
/**
* Evaluate the expression, optionally return results
@ -413,10 +516,10 @@ public:
protected:
/**
* Method to skip over whitespaces, count parsed lines too
* @param expr Pointer to expression cursor, gets advanced
* @param expr Current parsing context, advances on expression to be compiled
* @return First character after whitespaces where expr points
*/
virtual char skipWhites(const char*& expr);
virtual char skipWhites(ParsePoint& expr);
/**
* Helper method to conditionally convert to lower case
@ -495,59 +598,59 @@ protected:
/**
* Runs the parser and compiler for one (sub)expression
* @param expr Pointer to text to parse, gets advanced
* @param expr Current parsing context, advances on expression to be compiled
* @param stop Optional character expected after the expression
* @param nested User defined object to pass for nested parsing
* @return True if one expression was compiled and a separator follows
*/
bool runCompile(const char*& expr, char stop, GenObject* nested = 0);
bool runCompile(ParsePoint& expr, char stop, GenObject* nested = 0);
/**
* Runs the parser and compiler for one (sub)expression
* @param expr Pointer to text to parse, gets advanced
* @param expr Current parsing context, advances on expression to be compiled
* @param stop Optional list of possible characters expected after the expression
* @param nested User defined object to pass for nested parsing
* @return True if one expression was compiled and a separator follows
*/
virtual bool runCompile(const char*& expr, const char* stop = 0, GenObject* nested = 0);
virtual bool runCompile(ParsePoint& expr, const char* stop = 0, GenObject* nested = 0);
/**
* Skip over comments and whitespaces
* @param expr Pointer to expression cursor, gets advanced
* @param expr Current parsing context, advances on expression to be compiled
* @param context Pointer to arbitrary object to be passed to called methods
* @return First character after comments or whitespaces where expr points
*/
virtual char skipComments(const char*& expr, GenObject* context = 0);
virtual char skipComments(ParsePoint& expr, GenObject* context = 0);
/**
* Process top-level preprocessor directives
* @param expr Pointer to expression cursor, gets advanced
* @param expr Current parsing context, advances on expression to be compiled
* @param context Pointer to arbitrary object to be passed to called methods
* @return Number of expressions compiled, negative if no more directives
*/
virtual int preProcess(const char*& expr, GenObject* context = 0);
virtual int preProcess(ParsePoint& expr, GenObject* context = 0);
/**
* Returns next operator in the parsed text
* @param expr Pointer to text to parse, gets advanced if succeeds
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return Operator code, OpcNone on failure
*/
virtual Opcode getOperator(const char*& expr);
virtual Opcode getOperator(ParsePoint& expr);
/**
* Returns next unary operator in the parsed text
* @param expr Pointer to text to parse, gets advanced if succeeds
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return Operator code, OpcNone on failure
*/
virtual Opcode getUnaryOperator(const char*& expr);
virtual Opcode getUnaryOperator(ParsePoint& expr);
/**
* Returns next unary postfix operator in the parsed text
* @param expr Pointer to text to parse, gets advanced if succeeds
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @param precedence The precedence of the previous operator
* @return Operator code, OpcNone on failure
*/
virtual Opcode getPostfixOperator(const char*& expr, int precedence = 0);
virtual Opcode getPostfixOperator(ParsePoint& expr, int precedence = 0);
/**
* Helper method to get the canonical name of an operator
@ -572,58 +675,58 @@ protected:
/**
* Check if we are at an expression separator and optionally skip past it
* @param expr Pointer to text to check, gets advanced if asked to remove separator
* @param expr Current parsing context to check, advances on expression to be compiled if asked to remove separator
* @param remove True to skip past the found separator
* @return True if a separator was found
*/
virtual bool getSeparator(const char*& expr, bool remove);
virtual bool getSeparator(ParsePoint& expr, bool remove);
/**
* Get an instruction or block, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @param stop Optional character expected after the instruction
* @param nested User defined object passed from nested parsing
* @return True if succeeded, must add the operands internally
*/
virtual bool getInstruction(const char*& expr, char stop = 0, GenObject* nested = 0);
virtual bool getInstruction(ParsePoint& expr, char stop = 0, GenObject* nested = 0);
/**
* Get an operand, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @param endOk Consider reaching the end of string a success
* @param precedence The precedence of the previous operator
* @return True if succeeded, must add the operand internally
*/
virtual bool getOperand(const char*& expr, bool endOk = true, int precedence = 0);
virtual bool getOperand(ParsePoint& expr, bool endOk = true, int precedence = 0);
/**
* Get an inline simple type, usually string or number
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @param constOnly Return only inline constants
* @return True if succeeded, must add the operand internally
*/
virtual bool getSimple(const char*& expr, bool constOnly = false);
virtual bool getSimple(ParsePoint& expr, bool constOnly = false);
/**
* Get a numerical operand, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return True if succeeded, must add the operand internally
*/
virtual bool getNumber(const char*& expr);
virtual bool getNumber(ParsePoint& expr);
/**
* Get a string operand, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return True if succeeded, must add the operand internally
*/
virtual bool getString(const char*& expr);
virtual bool getString(ParsePoint& expr);
/**
* Get a function call, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return True if succeeded, must add the operand internally
*/
virtual bool getFunction(const char*& expr);
virtual bool getFunction(ParsePoint& expr);
/**
* Helper method - get a string, advance parsing pointer past it
@ -644,10 +747,10 @@ protected:
/**
* Get a field keyword, advance parsing pointer past it
* @param expr Pointer to text to parse, gets advanced on success
* @param expr Current parsing context, advances on expression to be compiled if it succeeds
* @return True if succeeded, must add the operand internally
*/
virtual bool getField(const char*& expr);
virtual bool getField(ParsePoint& expr);
/**
* Add an aready built operation to the expression and set its line number
@ -804,7 +907,7 @@ protected:
unsigned int m_lineNo;
private:
bool getOperandInternal(const char*& expr, bool endOk, int precedence);
bool getOperandInternal(ParsePoint& expr, bool endOk, int precedence);
ExpExtender* m_extender;
};