Added source file name and line number to compiled bytecode and operands.
Report the location in compile and runtime errors. git-svn-id: http://voip.null.ro/svn/yate@5074 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
d32634e03f
commit
cb19a8c2d3
|
@ -61,7 +61,6 @@ static const TokenDict s_operators_c[] =
|
|||
MAKEOP("&", And),
|
||||
MAKEOP("|", Or),
|
||||
MAKEOP("^", Xor),
|
||||
MAKEOP(".", Cat),
|
||||
MAKEOP("@", As),
|
||||
MAKEOP("=", Assign),
|
||||
{ 0, 0 }
|
||||
|
@ -146,12 +145,14 @@ bool ExpExtender::runAssign(ObjList& stack, const ExpOperation& oper, GenObject*
|
|||
|
||||
|
||||
ExpEvaluator::ExpEvaluator(const TokenDict* operators, const TokenDict* unaryOps)
|
||||
: m_operators(operators), m_unaryOps(unaryOps), m_inError(false), m_extender(0)
|
||||
: m_operators(operators), m_unaryOps(unaryOps),
|
||||
m_inError(false), m_lineNo(1), m_extender(0)
|
||||
{
|
||||
}
|
||||
|
||||
ExpEvaluator::ExpEvaluator(ExpEvaluator::Parser style)
|
||||
: m_operators(0), m_unaryOps(0), m_inError(false), m_extender(0)
|
||||
: m_operators(0), m_unaryOps(0),
|
||||
m_inError(false), m_lineNo(1), m_extender(0)
|
||||
{
|
||||
switch (style) {
|
||||
case C:
|
||||
|
@ -167,7 +168,7 @@ ExpEvaluator::ExpEvaluator(ExpEvaluator::Parser style)
|
|||
|
||||
ExpEvaluator::ExpEvaluator(const ExpEvaluator& original)
|
||||
: m_operators(original.m_operators), m_unaryOps(original.unaryOps()),
|
||||
m_inError(false), m_extender(0)
|
||||
m_inError(false), m_lineNo(original.lineNumber()), m_extender(0)
|
||||
{
|
||||
extender(original.extender());
|
||||
for (ObjList* l = original.m_opcodes.skipNull(); l; l = l->skipNext()) {
|
||||
|
@ -197,9 +198,34 @@ char ExpEvaluator::skipWhites(const char*& expr)
|
|||
{
|
||||
if (!expr)
|
||||
return 0;
|
||||
while (*expr==' ' || *expr=='\t' || *expr=='\r' || *expr=='\n')
|
||||
expr++;
|
||||
return *expr;
|
||||
char skip = '\0';
|
||||
for (;; *expr++) {
|
||||
char c = *expr;
|
||||
switch (*expr) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
skip = '\0';
|
||||
continue;
|
||||
case '\r':
|
||||
if (skip != c) {
|
||||
m_lineNo++;
|
||||
skip = '\n';
|
||||
}
|
||||
else
|
||||
skip = '\0';
|
||||
continue;
|
||||
case '\n':
|
||||
if (skip != c) {
|
||||
m_lineNo++;
|
||||
skip = '\r';
|
||||
}
|
||||
else
|
||||
skip = '\0';
|
||||
continue;
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ExpEvaluator::keywordChar(char c) const
|
||||
|
@ -208,7 +234,7 @@ bool ExpEvaluator::keywordChar(char c) const
|
|||
('0' <= c && c <= '9') || (c == '_');
|
||||
}
|
||||
|
||||
char ExpEvaluator::skipComments(const char*& expr, GenObject* context) const
|
||||
char ExpEvaluator::skipComments(const char*& expr, GenObject* context)
|
||||
{
|
||||
return skipWhites(expr);
|
||||
}
|
||||
|
@ -221,7 +247,6 @@ int ExpEvaluator::preProcess(const char*& expr, GenObject* context)
|
|||
ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr, const TokenDict* operators, bool caseInsensitive) const
|
||||
{
|
||||
XDebug(this,DebugAll,"getOperator('%.30s',%p,%s)",expr,operators,String::boolText(caseInsensitive));
|
||||
skipComments(expr);
|
||||
if (operators) {
|
||||
bool kw = keywordChar(*expr);
|
||||
for (const TokenDict* o = operators; o->token; o++) {
|
||||
|
@ -240,25 +265,34 @@ ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr, const TokenDic
|
|||
return OpcNone;
|
||||
}
|
||||
|
||||
bool ExpEvaluator::gotError(const char* error, const char* text) const
|
||||
bool ExpEvaluator::gotError(const char* error, const char* text, unsigned int line) const
|
||||
{
|
||||
if (!error) {
|
||||
if (!text)
|
||||
return false;
|
||||
error = "unknown error";
|
||||
}
|
||||
Debug(this,DebugWarn,"Evaluator error: %s%s%.50s",error,
|
||||
(text ? " at: " : ""),
|
||||
c_safe(text));
|
||||
if (!line)
|
||||
line = lineNumber();
|
||||
String lineNo;
|
||||
formatLineNo(lineNo,line);
|
||||
Debug(this,DebugWarn,"Evaluator error: %s in %s%s%.50s",error,
|
||||
lineNo.c_str(),(text ? " at: " : ""),c_safe(text));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExpEvaluator::gotError(const char* error, const char* text)
|
||||
bool ExpEvaluator::gotError(const char* error, const char* text, unsigned int line)
|
||||
{
|
||||
m_inError = true;
|
||||
return const_cast<const ExpEvaluator*>(this)->gotError(error,text);
|
||||
}
|
||||
|
||||
void ExpEvaluator::formatLineNo(String& buf, unsigned int line) const
|
||||
{
|
||||
buf.clear();
|
||||
buf << "line " << line;
|
||||
}
|
||||
|
||||
bool ExpEvaluator::getInstruction(const char*& expr, Opcode nested)
|
||||
{
|
||||
return false;
|
||||
|
@ -444,11 +478,13 @@ bool ExpEvaluator::getField(const char*& expr)
|
|||
|
||||
ExpEvaluator::Opcode ExpEvaluator::getOperator(const char*& expr)
|
||||
{
|
||||
skipComments(expr);
|
||||
return getOperator(expr,m_operators);
|
||||
}
|
||||
|
||||
ExpEvaluator::Opcode ExpEvaluator::getUnaryOperator(const char*& expr)
|
||||
{
|
||||
skipComments(expr);
|
||||
return getOperator(expr,m_unaryOps);
|
||||
}
|
||||
|
||||
|
@ -575,6 +611,7 @@ bool ExpEvaluator::runCompile(const char*& expr, char stop, Opcode nested)
|
|||
}
|
||||
if (inError())
|
||||
return false;
|
||||
skipComments(expr);
|
||||
oper = getOperator(expr);
|
||||
if (oper == OpcNone)
|
||||
return gotError("Operator or separator expected",expr);
|
||||
|
@ -630,10 +667,9 @@ bool ExpEvaluator::trySimplify()
|
|||
if (o->opcode() == OpcLAnd || o->opcode() == OpcAnd || o->opcode() == OpcMul) {
|
||||
if ((op1->opcode() == OpcPush && !op1->number() && op2->opcode() == OpcField) ||
|
||||
(op2->opcode() == OpcPush && !op2->number() && op1->opcode() == OpcField)) {
|
||||
if (o->opcode() == OpcLAnd)
|
||||
(m_opcodes+i)->set(new ExpOperation(false));
|
||||
else
|
||||
(m_opcodes+i)->set(new ExpOperation((long int)0));
|
||||
ExpOperation* newOp = (o->opcode() == OpcLAnd) ? new ExpOperation(false) : new ExpOperation((long int)0);
|
||||
newOp->lineNumber(o->lineNumber());
|
||||
(m_opcodes+i)->set(newOp);
|
||||
m_opcodes.remove(op1);
|
||||
m_opcodes.remove(op2);
|
||||
i -= 2;
|
||||
|
@ -644,7 +680,9 @@ bool ExpEvaluator::trySimplify()
|
|||
if (o->opcode() == OpcLOr) {
|
||||
if ((op1->opcode() == OpcPush && op1->number() && op2->opcode() == OpcField) ||
|
||||
(op2->opcode() == OpcPush && op2->number() && op1->opcode() == OpcField)) {
|
||||
(m_opcodes+i)->set(new ExpOperation(true));
|
||||
ExpOperation* newOp = new ExpOperation(true);
|
||||
newOp->lineNumber(o->lineNumber());
|
||||
(m_opcodes+i)->set(newOp);
|
||||
m_opcodes.remove(op1);
|
||||
m_opcodes.remove(op2);
|
||||
i -= 2;
|
||||
|
@ -658,7 +696,9 @@ bool ExpEvaluator::trySimplify()
|
|||
pushOne(stack,op2->clone());
|
||||
if (runOperation(stack,*o)) {
|
||||
// replace operators and operation with computed constant
|
||||
(m_opcodes+i)->set(popOne(stack));
|
||||
ExpOperation* newOp = popOne(stack);
|
||||
newOp->lineNumber(o->lineNumber());
|
||||
(m_opcodes+i)->set(newOp);
|
||||
m_opcodes.remove(op1);
|
||||
m_opcodes.remove(op2);
|
||||
i -= 2;
|
||||
|
@ -679,7 +719,9 @@ bool ExpEvaluator::trySimplify()
|
|||
pushOne(stack,op->clone());
|
||||
if (runOperation(stack,*o)) {
|
||||
// replace unary operator and operation with computed constant
|
||||
(m_opcodes+i)->set(popOne(stack));
|
||||
ExpOperation* newOp = popOne(stack);
|
||||
newOp->lineNumber(o->lineNumber());
|
||||
(m_opcodes+i)->set(newOp);
|
||||
m_opcodes.remove(op);
|
||||
i--;
|
||||
done = true;
|
||||
|
@ -701,6 +743,17 @@ bool ExpEvaluator::trySimplify()
|
|||
return done;
|
||||
}
|
||||
|
||||
void ExpEvaluator::addOpcode(ExpOperation* oper, unsigned int line)
|
||||
{
|
||||
if (!oper)
|
||||
return;
|
||||
DDebug(this,DebugAll,"addOpcode %u, %u",oper->opcode(),line);
|
||||
if (!line)
|
||||
line = lineNumber();
|
||||
oper->lineNumber(line);
|
||||
m_opcodes.append(oper);
|
||||
}
|
||||
|
||||
ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, bool barrier)
|
||||
{
|
||||
DDebug(this,DebugAll,"addOpcode %u",oper);
|
||||
|
@ -715,6 +768,7 @@ ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, bool barrier)
|
|||
}
|
||||
}
|
||||
ExpOperation* op = new ExpOperation(oper,0,ExpOperation::nonInteger(),barrier);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -723,6 +777,7 @@ ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, long int value,
|
|||
{
|
||||
DDebug(this,DebugAll,"addOpcode %u %lu",oper,value);
|
||||
ExpOperation* op = new ExpOperation(oper,0,value,barrier);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -731,6 +786,7 @@ ExpOperation* ExpEvaluator::addOpcode(ExpEvaluator::Opcode oper, const String& n
|
|||
{
|
||||
DDebug(this,DebugAll,"addOpcode %u '%s' %ld",oper,name.c_str(),value);
|
||||
ExpOperation* op = new ExpOperation(oper,name,value,barrier);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -739,6 +795,7 @@ ExpOperation* ExpEvaluator::addOpcode(const String& value)
|
|||
{
|
||||
DDebug(this,DebugAll,"addOpcode ='%s'",value.c_str());
|
||||
ExpOperation* op = new ExpOperation(value);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -747,6 +804,7 @@ ExpOperation* ExpEvaluator::addOpcode(long int value)
|
|||
{
|
||||
DDebug(this,DebugAll,"addOpcode =%ld",value);
|
||||
ExpOperation* op = new ExpOperation(value);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -755,6 +813,7 @@ ExpOperation* ExpEvaluator::addOpcode(bool value)
|
|||
{
|
||||
DDebug(this,DebugAll,"addOpcode =%s",String::boolText(value));
|
||||
ExpOperation* op = new ExpOperation(value);
|
||||
op->lineNumber(lineNumber());
|
||||
m_opcodes.append(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -769,6 +828,17 @@ ExpOperation* ExpEvaluator::popOpcode()
|
|||
return static_cast<ExpOperation*>(l->remove(false));
|
||||
}
|
||||
|
||||
unsigned int ExpEvaluator::getLineOf(ExpOperation* op1, ExpOperation* op2, ExpOperation* op3)
|
||||
{
|
||||
if (op1 && op1->lineNumber())
|
||||
return op1->lineNumber();
|
||||
if (op2 && op2->lineNumber())
|
||||
return op2->lineNumber();
|
||||
if (op3 && op3->lineNumber())
|
||||
return op3->lineNumber();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExpEvaluator::pushOne(ObjList& stack, ExpOperation* oper)
|
||||
{
|
||||
if (oper)
|
||||
|
@ -858,13 +928,13 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
switch (oper.opcode()) {
|
||||
case OpcDiv:
|
||||
case OpcMod:
|
||||
if (!op2->number())
|
||||
return gotError("Division by zero");
|
||||
return gotError("Division by zero",oper.lineNumber());
|
||||
case OpcAdd:
|
||||
if (op1->isInteger() && op2->isInteger())
|
||||
break;
|
||||
|
@ -953,7 +1023,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
bool val = false;
|
||||
switch (oper.opcode()) {
|
||||
|
@ -979,7 +1049,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
String val = *op1 + *op2;
|
||||
TelEngine::destruct(op1);
|
||||
|
@ -995,7 +1065,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,op1->clone(*op2));
|
||||
TelEngine::destruct(op1);
|
||||
|
@ -1008,7 +1078,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
{
|
||||
ExpOperation* op = popValue(stack,context);
|
||||
if (!op)
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
long int val = op->number();
|
||||
TelEngine::destruct(op);
|
||||
switch (oper.opcode()) {
|
||||
|
@ -1028,7 +1098,8 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
}
|
||||
break;
|
||||
case OpcFunc:
|
||||
return runFunction(stack,oper,context) || gotError("Function call failed");
|
||||
return runFunction(stack,oper,context) ||
|
||||
gotError("Function '" + oper.name() + "' call failed",oper.lineNumber());
|
||||
case OpcIncPre:
|
||||
case OpcDecPre:
|
||||
case OpcIncPost:
|
||||
|
@ -1036,10 +1107,10 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
{
|
||||
ExpOperation* fld = popOne(stack);
|
||||
if (!fld)
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
if (fld->opcode() != OpcField) {
|
||||
TelEngine::destruct(fld);
|
||||
return gotError("Expecting LValue in operator");
|
||||
return gotError("Expecting LValue in operator",oper.lineNumber());
|
||||
}
|
||||
ExpOperation* val = 0;
|
||||
if (!(runField(stack,*fld,context) && (val = popOne(stack)))) {
|
||||
|
@ -1072,7 +1143,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
TelEngine::destruct(fld);
|
||||
if (!ok) {
|
||||
TelEngine::destruct(val);
|
||||
return gotError("Assignment failed");
|
||||
return gotError("Assignment failed",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,val);
|
||||
}
|
||||
|
@ -1084,12 +1155,12 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!fld || !val) {
|
||||
TelEngine::destruct(fld);
|
||||
TelEngine::destruct(val);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
if (fld->opcode() != OpcField) {
|
||||
TelEngine::destruct(fld);
|
||||
TelEngine::destruct(val);
|
||||
return gotError("Expecting LValue in assignment");
|
||||
return gotError("Expecting LValue in assignment",oper.lineNumber());
|
||||
}
|
||||
ExpOperation* op = val->clone(fld->name());
|
||||
TelEngine::destruct(fld);
|
||||
|
@ -1097,7 +1168,7 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
TelEngine::destruct(op);
|
||||
if (!ok) {
|
||||
TelEngine::destruct(val);
|
||||
return gotError("Assignment failed");
|
||||
return gotError("Assignment failed",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,val);
|
||||
}
|
||||
|
@ -1110,12 +1181,12 @@ bool ExpEvaluator::runOperation(ObjList& stack, const ExpOperation& oper, GenObj
|
|||
if (!fld || !val) {
|
||||
TelEngine::destruct(fld);
|
||||
TelEngine::destruct(val);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
if (fld->opcode() != OpcField) {
|
||||
TelEngine::destruct(fld);
|
||||
TelEngine::destruct(val);
|
||||
return gotError("Expecting LValue in assignment");
|
||||
return gotError("Expecting LValue in assignment",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,fld->clone());
|
||||
pushOne(stack,fld);
|
||||
|
@ -1143,7 +1214,7 @@ bool ExpEvaluator::runFunction(ObjList& stack, const ExpOperation& oper, GenObje
|
|||
for (long int i = oper.number(); i; i--) {
|
||||
ExpOperation* o = popValue(stack,context);
|
||||
if (!o)
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
res = String((char)o->number()) + res;
|
||||
TelEngine::destruct(o);
|
||||
}
|
||||
|
@ -1152,7 +1223,7 @@ bool ExpEvaluator::runFunction(ObjList& stack, const ExpOperation& oper, GenObje
|
|||
}
|
||||
if (oper.name() == YSTRING("now")) {
|
||||
if (oper.number())
|
||||
return gotError("Function expects no arguments");
|
||||
return gotError("Function expects no arguments",oper.lineNumber());
|
||||
pushOne(stack,new ExpOperation((long int)Time::secNow()));
|
||||
return true;
|
||||
}
|
||||
|
@ -1316,10 +1387,20 @@ void ExpEvaluator::dump(const ObjList& codes, String& res) const
|
|||
}
|
||||
|
||||
|
||||
ExpOperation* ExpOperation::clone(const char* name) const
|
||||
{
|
||||
ExpOperation* op = new ExpOperation(*this,name);
|
||||
op->lineNumber(lineNumber());
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
ExpOperation* ExpFunction::clone(const char* name) const
|
||||
{
|
||||
XDebug(DebugInfo,"ExpFunction::clone('%s') [%p]",name,this);
|
||||
return new ExpFunction(name,number());
|
||||
ExpFunction* op = new ExpFunction(name,number());
|
||||
op->lineNumber(lineNumber());
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1329,7 +1410,9 @@ ExpOperation* ExpWrapper::clone(const char* name) const
|
|||
RefObject* r = YOBJECT(RefObject,object());
|
||||
if (r)
|
||||
r->ref();
|
||||
return new ExpWrapper(object(),name);
|
||||
ExpWrapper* op = new ExpWrapper(object(),name);
|
||||
op->lineNumber(lineNumber());
|
||||
return op;
|
||||
}
|
||||
|
||||
void* ExpWrapper::getObject(const String& name) const
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
OpcBegin = OpcPrivate + 1,
|
||||
OpcEnd,
|
||||
OpcIndex,
|
||||
OpcFieldOf,
|
||||
OpcTypeof,
|
||||
OpcNew,
|
||||
OpcFor,
|
||||
|
@ -103,11 +104,12 @@ public:
|
|||
JsObject* parseArray(const char*& expr, bool constOnly);
|
||||
JsObject* parseObject(const char*& expr, bool constOnly);
|
||||
protected:
|
||||
virtual void formatLineNo(String& buf, unsigned int line) const;
|
||||
virtual bool getString(const char*& 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) 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, Opcode nested);
|
||||
virtual bool getSimple(const char*& expr, bool constOnly = false);
|
||||
|
@ -153,6 +155,7 @@ private:
|
|||
#define MAKEOP(s,o) { s, JsCode::Opc ## o }
|
||||
static const TokenDict s_operators[] =
|
||||
{
|
||||
MAKEOP(".",FieldOf),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -384,6 +387,16 @@ bool JsCode::link()
|
|||
return true;
|
||||
}
|
||||
|
||||
void JsCode::formatLineNo(String& buf, unsigned int line) const
|
||||
{
|
||||
unsigned int fnum = (line >> 24) & 0xff;
|
||||
if (!fnum)
|
||||
return ExpEvaluator::formatLineNo(buf,line);
|
||||
buf.clear();
|
||||
const GenObject* file = m_included[fnum - 1];
|
||||
buf << (file ? file->toString().c_str() : "???") << ":" << (line & 0xffffff);
|
||||
}
|
||||
|
||||
bool JsCode::getString(const char*& expr)
|
||||
{
|
||||
if (inError())
|
||||
|
@ -519,7 +532,7 @@ int JsCode::getKeyword(const char* str) const
|
|||
return len;
|
||||
}
|
||||
|
||||
char JsCode::skipComments(const char*& expr, GenObject* context) const
|
||||
char JsCode::skipComments(const char*& expr, GenObject* context)
|
||||
{
|
||||
char c = skipWhites(expr);
|
||||
while (c == '/') {
|
||||
|
@ -555,30 +568,32 @@ bool JsCode::preProcessInclude(const char*& expr, bool once, GenObject* context)
|
|||
return false;
|
||||
char c = skipComments(expr);
|
||||
if (c == '"' || c == '\'') {
|
||||
char sep = c;
|
||||
const char* start = ++expr;
|
||||
while ((c = *expr++)) {
|
||||
if (c != sep)
|
||||
continue;
|
||||
String str(start,expr-start-1);
|
||||
String str;
|
||||
if (ExpEvaluator::getString(expr,str)) {
|
||||
DDebug(this,DebugAll,"Found include '%s'",str.safe());
|
||||
parser->adjustPath(str);
|
||||
str.trimSpaces();
|
||||
bool ok = !str.null();
|
||||
if (ok) {
|
||||
bool already = m_included.find(str);
|
||||
if (!(once && already)) {
|
||||
int idx = m_included.index(str);
|
||||
if (!(once && (idx >= 0))) {
|
||||
if (idx < 0) {
|
||||
String* s = new String(str);
|
||||
m_included.append(s);
|
||||
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;
|
||||
m_depth++;
|
||||
ok = parser->parseFile(str,true);
|
||||
m_depth--;
|
||||
if (ok && !already)
|
||||
m_included.append(new String(str));
|
||||
m_lineNo = savedLine;
|
||||
}
|
||||
}
|
||||
return ok || gotError("Failed to include " + str);
|
||||
}
|
||||
expr--;
|
||||
return gotError("Expecting string end");
|
||||
return false;
|
||||
}
|
||||
return gotError("Expecting include file",expr);
|
||||
}
|
||||
|
@ -587,6 +602,7 @@ int JsCode::preProcess(const char*& expr, GenObject* context)
|
|||
{
|
||||
int rval = -1;
|
||||
for (;;) {
|
||||
skipComments(expr);
|
||||
JsOpcode opc = (JsOpcode)ExpEvaluator::getOperator(expr,s_preProc);
|
||||
switch (opc) {
|
||||
case OpcInclude:
|
||||
|
@ -628,6 +644,7 @@ bool JsCode::getInstruction(const char*& expr, Opcode nested)
|
|||
return true;
|
||||
}
|
||||
const char* saved = expr;
|
||||
skipComments(expr);
|
||||
Opcode op = ExpEvaluator::getOperator(expr,s_instr);
|
||||
switch ((JsOpcode)op) {
|
||||
case (JsOpcode)OpcNone:
|
||||
|
@ -655,6 +672,7 @@ bool JsCode::getInstruction(const char*& expr, Opcode nested)
|
|||
if (skipComments(expr) == ';')
|
||||
expr++;
|
||||
const char* save = expr;
|
||||
skipComments(expr);
|
||||
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcElse) {
|
||||
ExpOperation* jump = addOpcode((Opcode)OpcJump,++m_label);
|
||||
addOpcode(OpcLabel,cond->number());
|
||||
|
@ -702,6 +720,7 @@ bool JsCode::getInstruction(const char*& expr, Opcode nested)
|
|||
if (!runCompile(expr,0,op))
|
||||
return false;
|
||||
{
|
||||
skipComments(expr);
|
||||
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcCatch) {
|
||||
if (skipComments(expr) != '(')
|
||||
return gotError("Expecting '('",expr);
|
||||
|
@ -712,6 +731,7 @@ bool JsCode::getInstruction(const char*& expr, Opcode nested)
|
|||
if (!runCompile(++expr))
|
||||
return false;
|
||||
}
|
||||
skipComments(expr);
|
||||
if ((JsOpcode)ExpEvaluator::getOperator(expr,s_instr) == OpcFinally) {
|
||||
if (!runCompile(expr))
|
||||
return false;
|
||||
|
@ -766,6 +786,7 @@ bool JsCode::getNumber(const char*& expr)
|
|||
{
|
||||
if (inError())
|
||||
return false;
|
||||
skipComments(expr);
|
||||
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_bools)) {
|
||||
case OpcFalse:
|
||||
addOpcode(false);
|
||||
|
@ -784,6 +805,7 @@ ExpEvaluator::Opcode JsCode::getOperator(const char*& expr)
|
|||
if (inError())
|
||||
return OpcNone;
|
||||
XDebug(this,DebugAll,"JsCode::getOperator '%.30s'",expr);
|
||||
skipComments(expr);
|
||||
Opcode op = ExpEvaluator::getOperator(expr,s_operators);
|
||||
if (OpcNone != op)
|
||||
return op;
|
||||
|
@ -795,6 +817,7 @@ ExpEvaluator::Opcode JsCode::getUnaryOperator(const char*& expr)
|
|||
if (inError())
|
||||
return OpcNone;
|
||||
XDebug(this,DebugAll,"JsCode::getUnaryOperator '%.30s'",expr);
|
||||
skipComments(expr);
|
||||
Opcode op = ExpEvaluator::getOperator(expr,s_unaryOps);
|
||||
if (OpcNone != op)
|
||||
return op;
|
||||
|
@ -816,6 +839,7 @@ ExpEvaluator::Opcode JsCode::getPostfixOperator(const char*& expr)
|
|||
expr++;
|
||||
return (Opcode)OpcIndex;
|
||||
}
|
||||
skipComments(expr);
|
||||
Opcode op = ExpEvaluator::getOperator(expr,s_postfixOps);
|
||||
if (OpcNone != op)
|
||||
return op;
|
||||
|
@ -846,6 +870,8 @@ int JsCode::getPrecedence(ExpEvaluator::Opcode oper) const
|
|||
case OpcNew:
|
||||
case OpcIndex:
|
||||
return 12;
|
||||
case OpcFieldOf:
|
||||
return 13;
|
||||
default:
|
||||
return ExpEvaluator::getPrecedence(oper);
|
||||
}
|
||||
|
@ -874,7 +900,7 @@ bool JsCode::getSimple(const char*& expr, bool constOnly)
|
|||
jso = parseObject(expr,constOnly);
|
||||
if (!jso)
|
||||
return false;
|
||||
m_opcodes.append(new ExpWrapper(jso));
|
||||
addOpcode(new ExpWrapper(jso));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -976,7 +1002,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
}
|
||||
if (!b) {
|
||||
TelEngine::destruct(op);
|
||||
return gotError("ExpEvaluator stack underflow");
|
||||
return gotError("ExpEvaluator stack underflow",oper.lineNumber());
|
||||
}
|
||||
b->clear();
|
||||
pushOne(stack,op);
|
||||
|
@ -989,23 +1015,42 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("Stack underflow");
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
}
|
||||
if (op1->opcode() != OpcField) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("Expecting field name");
|
||||
return gotError("Expecting field name",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,new ExpOperation(OpcField,op1->name() + "." + *op2));
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
}
|
||||
break;
|
||||
case OpcFieldOf:
|
||||
{
|
||||
ExpOperation* op2 = popOne(stack);
|
||||
ExpOperation* op1 = popOne(stack);
|
||||
if (!op1 || !op2) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
}
|
||||
if (op1->opcode() != OpcField || op2->opcode() != OpcField) {
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
return gotError("Expecting field names",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,new ExpOperation(OpcField,op1->name() + "." + op2->name()));
|
||||
TelEngine::destruct(op1);
|
||||
TelEngine::destruct(op2);
|
||||
}
|
||||
break;
|
||||
case OpcTypeof:
|
||||
{
|
||||
ExpOperation* op = popValue(stack,context);
|
||||
if (!op)
|
||||
return gotError("Stack underflow");
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
switch (op->opcode()) {
|
||||
case OpcPush:
|
||||
{
|
||||
|
@ -1031,7 +1076,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
{
|
||||
ExpOperation* op = popOne(stack);
|
||||
if (!op)
|
||||
return gotError("Stack underflow");
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
switch (op->opcode()) {
|
||||
case OpcField:
|
||||
break;
|
||||
|
@ -1046,7 +1091,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
// fall through
|
||||
default:
|
||||
TelEngine::destruct(op);
|
||||
return gotError("Expecting class name");
|
||||
return gotError("Expecting class name",oper.lineNumber());
|
||||
}
|
||||
ExpFunction ctr(op->name(),op->number());
|
||||
TelEngine::destruct(op);
|
||||
|
@ -1057,7 +1102,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
{
|
||||
ExpOperation* op = popOne(stack);
|
||||
if (!op)
|
||||
return gotError("Stack underflow");
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
bool ok = false;
|
||||
while (ExpOperation* drop = popAny(stack)) {
|
||||
JsOpcode c = (JsOpcode)drop->opcode();
|
||||
|
@ -1068,7 +1113,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
}
|
||||
}
|
||||
if (!ok)
|
||||
return gotError("Uncaught exception: " + *op);
|
||||
return gotError("Uncaught exception: " + *op,oper.lineNumber());
|
||||
pushOne(stack,op);
|
||||
}
|
||||
break;
|
||||
|
@ -1089,7 +1134,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
}
|
||||
if (!ok) {
|
||||
TelEngine::destruct(op);
|
||||
return gotError("Return outside function call");
|
||||
return gotError("Return outside function call",oper.lineNumber());
|
||||
}
|
||||
pushOne(stack,op);
|
||||
}
|
||||
|
@ -1101,7 +1146,7 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
{
|
||||
ExpOperation* op = popValue(stack,context);
|
||||
if (!op)
|
||||
return gotError("Stack underflow");
|
||||
return gotError("Stack underflow",oper.lineNumber());
|
||||
bool val = op->number() != 0;
|
||||
TelEngine::destruct(op);
|
||||
switch ((JsOpcode)oper.opcode()) {
|
||||
|
@ -1126,11 +1171,11 @@ bool JsCode::runOperation(ObjList& stack, const ExpOperation& oper, GenObject* c
|
|||
case OpcJump:
|
||||
case OpcJumpTrue:
|
||||
case OpcJumpFalse:
|
||||
return jumpToLabel(oper.number(),context) || gotError("Label not found");
|
||||
return jumpToLabel(oper.number(),context) || gotError("Label not found",oper.lineNumber());
|
||||
case OpcJRel:
|
||||
case OpcJRelTrue:
|
||||
case OpcJRelFalse:
|
||||
return jumpRelative(oper.number(),context) || gotError("Relative jump failed");
|
||||
return jumpRelative(oper.number(),context) || gotError("Relative jump failed",oper.lineNumber());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -283,6 +283,13 @@ public:
|
|||
inline bool inError() const
|
||||
{ return m_inError; }
|
||||
|
||||
/**
|
||||
* Retrieve the number of line currently being parsed
|
||||
* @return Number of current parsed line, 1 is the first line
|
||||
*/
|
||||
inline unsigned int lineNumber() const
|
||||
{ return m_lineNo; }
|
||||
|
||||
/**
|
||||
* Check if the expression is empty (no operands or operators)
|
||||
* @return True if the expression is completely empty
|
||||
|
@ -346,6 +353,15 @@ public:
|
|||
*/
|
||||
void extender(ExpExtender* ext);
|
||||
|
||||
/**
|
||||
* Retrieve the line number from one to three operands
|
||||
* @param op1 First operand
|
||||
* @param op2 Optional second operand
|
||||
* @param op3 Optional third operand
|
||||
* @return Line number at compile time, zero if not found
|
||||
*/
|
||||
static unsigned int getLineOf(ExpOperation* op1, ExpOperation* op2 = 0, ExpOperation* op3 = 0);
|
||||
|
||||
/**
|
||||
* Push an operand on an evaluation stack
|
||||
* @param stack Evaluation stack to remove the operand from
|
||||
|
@ -395,11 +411,11 @@ public:
|
|||
|
||||
protected:
|
||||
/**
|
||||
* Helper method to skip over whitespaces
|
||||
* Method to skip over whitespaces, count parsed lines too
|
||||
* @param expr Pointer to expression cursor, gets advanced
|
||||
* @return First character after whitespaces where expr points
|
||||
*/
|
||||
static char skipWhites(const char*& expr);
|
||||
virtual char skipWhites(const char*& expr);
|
||||
|
||||
/**
|
||||
* Helper method to conditionally convert to lower case
|
||||
|
@ -437,17 +453,44 @@ protected:
|
|||
* Helper method to display debugging errors internally
|
||||
* @param error Text of the error
|
||||
* @param text Optional text that caused the error
|
||||
* @param line Number of line generating the error, zero for parsing errors
|
||||
* @return Always returns false
|
||||
*/
|
||||
bool gotError(const char* error = 0, const char* text = 0) const;
|
||||
bool gotError(const char* error = 0, const char* text = 0, unsigned int line = 0) const;
|
||||
|
||||
/**
|
||||
* Helper method to set error flag and display debugging errors internally
|
||||
* @param error Text of the error
|
||||
* @param text Optional text that caused the error
|
||||
* @param line Number of line generating the error, zero for parsing errors
|
||||
* @return Always returns false
|
||||
*/
|
||||
bool gotError(const char* error = 0, const char* text = 0);
|
||||
bool gotError(const char* error = 0, const char* text = 0, unsigned int line = 0);
|
||||
|
||||
/**
|
||||
* Helper method to display debugging errors internally
|
||||
* @param error Text of the error
|
||||
* @param line Number of line generating the error, zero for parsing errors
|
||||
* @return Always returns false
|
||||
*/
|
||||
inline bool gotError(const char* error, unsigned int line) const
|
||||
{ return gotError(error, 0, line); }
|
||||
|
||||
/**
|
||||
* Helper method to set error flag and display debugging errors internally
|
||||
* @param error Text of the error
|
||||
* @param line Number of line generating the error, zero for parsing errors
|
||||
* @return Always returns false
|
||||
*/
|
||||
inline bool gotError(const char* error, unsigned int line)
|
||||
{ return gotError(error, 0, line); }
|
||||
|
||||
/**
|
||||
* Formats a line number to display in error messages
|
||||
* @param buf String buffer used to return the value
|
||||
* @param line Line number to format
|
||||
*/
|
||||
virtual void formatLineNo(String& buf, unsigned int line) const;
|
||||
|
||||
/**
|
||||
* Runs the parser and compiler for one (sub)expression
|
||||
|
@ -464,7 +507,7 @@ protected:
|
|||
* @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) const;
|
||||
virtual char skipComments(const char*& expr, GenObject* context = 0);
|
||||
|
||||
/**
|
||||
* Process top-level preprocessor directives
|
||||
|
@ -593,6 +636,13 @@ protected:
|
|||
*/
|
||||
virtual bool getField(const char*& expr);
|
||||
|
||||
/**
|
||||
* Add an aready built operation to the expression and set its line number
|
||||
* @param oper Operation to add
|
||||
* @param line Line number where operation was compiled, zero to used parsing point
|
||||
*/
|
||||
void addOpcode(ExpOperation* oper, unsigned int line = 0);
|
||||
|
||||
/**
|
||||
* Add a simple operator to the expression
|
||||
* @param oper Operator code to add
|
||||
|
@ -728,6 +778,11 @@ protected:
|
|||
*/
|
||||
bool m_inError;
|
||||
|
||||
/**
|
||||
* Current line index
|
||||
*/
|
||||
unsigned int m_lineNo;
|
||||
|
||||
private:
|
||||
ExpExtender* m_extender;
|
||||
};
|
||||
|
@ -755,7 +810,7 @@ public:
|
|||
inline ExpOperation(const ExpOperation& original)
|
||||
: NamedString(original.name(),original),
|
||||
m_opcode(original.opcode()), m_number(original.number()),
|
||||
m_barrier(original.barrier())
|
||||
m_lineNo(0), m_barrier(original.barrier())
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -766,7 +821,7 @@ public:
|
|||
inline ExpOperation(const ExpOperation& original, const char* name)
|
||||
: NamedString(name,original),
|
||||
m_opcode(original.opcode()), m_number(original.number()),
|
||||
m_barrier(original.barrier())
|
||||
m_lineNo(0), m_barrier(original.barrier())
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -779,7 +834,7 @@ public:
|
|||
: NamedString(name,value),
|
||||
m_opcode(ExpEvaluator::OpcPush),
|
||||
m_number(autoNum ? value.toLong(nonInteger()) : nonInteger()),
|
||||
m_barrier(false)
|
||||
m_lineNo(0), m_barrier(false)
|
||||
{ if (autoNum && value.isBoolean()) m_number = value.toBoolean() ? 1 : 0; }
|
||||
|
||||
/**
|
||||
|
@ -789,7 +844,7 @@ public:
|
|||
*/
|
||||
inline explicit ExpOperation(const char* value, const char* name = 0)
|
||||
: NamedString(name,value),
|
||||
m_opcode(ExpEvaluator::OpcPush), m_number(nonInteger()), m_barrier(false)
|
||||
m_opcode(ExpEvaluator::OpcPush), m_number(nonInteger()), m_lineNo(0), m_barrier(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -799,7 +854,8 @@ public:
|
|||
*/
|
||||
inline explicit ExpOperation(long int value, const char* name = 0)
|
||||
: NamedString(name,"NaN"),
|
||||
m_opcode(ExpEvaluator::OpcPush), m_number(value), m_barrier(false)
|
||||
m_opcode(ExpEvaluator::OpcPush),
|
||||
m_number(value), m_lineNo(0), m_barrier(false)
|
||||
{ if (value != nonInteger()) String::operator=((int)value); }
|
||||
|
||||
/**
|
||||
|
@ -809,7 +865,8 @@ public:
|
|||
*/
|
||||
inline explicit ExpOperation(bool value, const char* name = 0)
|
||||
: NamedString(name,String::boolText(value)),
|
||||
m_opcode(ExpEvaluator::OpcPush), m_number(value ? 1 : 0), m_barrier(false)
|
||||
m_opcode(ExpEvaluator::OpcPush),
|
||||
m_number(value ? 1 : 0), m_lineNo(0), m_barrier(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -821,7 +878,7 @@ public:
|
|||
*/
|
||||
inline ExpOperation(ExpEvaluator::Opcode oper, const char* name = 0, long int value = nonInteger(), bool barrier = false)
|
||||
: NamedString(name,""),
|
||||
m_opcode(oper), m_number(value), m_barrier(barrier)
|
||||
m_opcode(oper), m_number(value), m_lineNo(0), m_barrier(barrier)
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -833,7 +890,7 @@ public:
|
|||
*/
|
||||
inline ExpOperation(ExpEvaluator::Opcode oper, const char* name, const char* value, bool barrier = false)
|
||||
: NamedString(name,value),
|
||||
m_opcode(oper), m_number(nonInteger()), m_barrier(barrier)
|
||||
m_opcode(oper), m_number(nonInteger()), m_lineNo(0), m_barrier(barrier)
|
||||
{ }
|
||||
|
||||
/**
|
||||
|
@ -864,6 +921,20 @@ public:
|
|||
inline bool barrier() const
|
||||
{ return m_barrier; }
|
||||
|
||||
/**
|
||||
* Retrieve the line number where the operation was compiled from
|
||||
* @return Line number, zero if unknown
|
||||
*/
|
||||
inline unsigned int lineNumber() const
|
||||
{ return m_lineNo; }
|
||||
|
||||
/**
|
||||
* Set the line number where the operation was compiled from
|
||||
* @param line Number of the compiled line
|
||||
*/
|
||||
inline void lineNumber(unsigned int line)
|
||||
{ m_lineNo = line; }
|
||||
|
||||
/**
|
||||
* Number assignment operator
|
||||
* @param num Numeric value to assign to the operation
|
||||
|
@ -877,8 +948,7 @@ public:
|
|||
* @param name Name of the cloned operation
|
||||
* @return New operation instance
|
||||
*/
|
||||
virtual ExpOperation* clone(const char* name) const
|
||||
{ return new ExpOperation(*this,name); }
|
||||
virtual ExpOperation* clone(const char* name) const;
|
||||
|
||||
/**
|
||||
* Clone method
|
||||
|
@ -890,6 +960,7 @@ public:
|
|||
private:
|
||||
ExpEvaluator::Opcode m_opcode;
|
||||
long int m_number;
|
||||
unsigned int m_lineNo;
|
||||
bool m_barrier;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue