Added native JSON parser and stringifier.
git-svn-id: http://yate.null.ro/svn/yate/trunk@5869 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
0842fc2581
commit
9b6259ac82
|
@ -174,8 +174,8 @@ public:
|
||||||
bool link();
|
bool link();
|
||||||
inline bool traceable() const
|
inline bool traceable() const
|
||||||
{ return m_traceable; }
|
{ return m_traceable; }
|
||||||
JsObject* parseArray(ParsePoint& expr, bool constOnly);
|
JsObject* parseArray(ParsePoint& expr, bool constOnly, Mutex* mtx);
|
||||||
JsObject* parseObject(ParsePoint& expr, bool constOnly);
|
JsObject* parseObject(ParsePoint& expr, bool constOnly, Mutex* mtx);
|
||||||
inline const NamedList& pragmas() const
|
inline const NamedList& pragmas() const
|
||||||
{ return m_pragmas; }
|
{ return m_pragmas; }
|
||||||
inline static unsigned int getLineNo(unsigned int line)
|
inline static unsigned int getLineNo(unsigned int line)
|
||||||
|
@ -227,6 +227,7 @@ private:
|
||||||
bool parseVar(ParsePoint& expr);
|
bool parseVar(ParsePoint& expr);
|
||||||
bool parseTry(ParsePoint& expr, GenObject* nested);
|
bool parseTry(ParsePoint& expr, GenObject* nested);
|
||||||
bool parseFuncDef(ParsePoint& expr, bool publish);
|
bool parseFuncDef(ParsePoint& expr, bool publish);
|
||||||
|
bool parseSimple(ParsePoint& expr, bool constOnly, Mutex* mtx = 0);
|
||||||
bool evalList(ObjList& stack, GenObject* context) const;
|
bool evalList(ObjList& stack, GenObject* context) const;
|
||||||
bool evalVector(ObjList& stack, GenObject* context) const;
|
bool evalVector(ObjList& stack, GenObject* context) const;
|
||||||
bool jumpToLabel(long int label, GenObject* context) const;
|
bool jumpToLabel(long int label, GenObject* context) const;
|
||||||
|
@ -1908,10 +1909,15 @@ bool JsCode::getSeparator(ParsePoint& expr, bool remove)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
||||||
|
{
|
||||||
|
return parseSimple(expr,constOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsCode::parseSimple(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||||
{
|
{
|
||||||
if (inError())
|
if (inError())
|
||||||
return false;
|
return false;
|
||||||
XDebug(this,DebugAll,"JsCode::getSimple(%s) '%.30s'",String::boolText(constOnly),(const char*)expr);
|
XDebug(this,DebugAll,"JsCode::parseSimple(%s) '%.30s'",String::boolText(constOnly),(const char*)expr);
|
||||||
skipComments(expr);
|
skipComments(expr);
|
||||||
ParsePoint save = expr;
|
ParsePoint save = expr;
|
||||||
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_constants)) {
|
switch ((JsOpcode)ExpEvaluator::getOperator(expr,s_constants)) {
|
||||||
|
@ -1936,9 +1942,9 @@ bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
JsObject* jso = parseArray(expr,constOnly);
|
JsObject* jso = parseArray(expr,constOnly,mtx);
|
||||||
if (!jso)
|
if (!jso)
|
||||||
jso = parseObject(expr,constOnly);
|
jso = parseObject(expr,constOnly,mtx);
|
||||||
if (!jso)
|
if (!jso)
|
||||||
return ExpEvaluator::getSimple(expr,constOnly);
|
return ExpEvaluator::getSimple(expr,constOnly);
|
||||||
addOpcode(new ExpWrapper(ExpEvaluator::OpcCopy,jso));
|
addOpcode(new ExpWrapper(ExpEvaluator::OpcCopy,jso));
|
||||||
|
@ -1946,12 +1952,12 @@ bool JsCode::getSimple(ParsePoint& expr, bool constOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an inline Javascript Array: [ item1, item2, ... ]
|
// Parse an inline Javascript Array: [ item1, item2, ... ]
|
||||||
JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||||
{
|
{
|
||||||
if (skipComments(expr) != '[')
|
if (skipComments(expr) != '[')
|
||||||
return 0;
|
return 0;
|
||||||
expr++;
|
expr++;
|
||||||
JsArray* jsa = new JsArray(0,"[object Array]");
|
JsArray* jsa = new JsArray(mtx,"[object Array]");
|
||||||
for (bool first = true; ; first = false) {
|
for (bool first = true; ; first = false) {
|
||||||
if (skipComments(expr) == ']') {
|
if (skipComments(expr) == ']') {
|
||||||
expr++;
|
expr++;
|
||||||
|
@ -1986,7 +1992,7 @@ JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
||||||
jsa->push(new ExpWrapper(0,"undefined"));
|
jsa->push(new ExpWrapper(0,"undefined"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bool ok = constOnly ? getSimple(expr,true) : getOperand(expr,false);
|
bool ok = constOnly ? parseSimple(expr,true,mtx) : getOperand(expr,false);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
TelEngine::destruct(jsa);
|
TelEngine::destruct(jsa);
|
||||||
break;
|
break;
|
||||||
|
@ -2001,12 +2007,12 @@ JsObject* JsCode::parseArray(ParsePoint& expr, bool constOnly)
|
||||||
|
|
||||||
|
|
||||||
// Parse an inline Javascript Object: { prop1: value1, "prop 2": value2, ... }
|
// Parse an inline Javascript Object: { prop1: value1, "prop 2": value2, ... }
|
||||||
JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly)
|
JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly, Mutex* mtx)
|
||||||
{
|
{
|
||||||
if (skipComments(expr) != '{')
|
if (skipComments(expr) != '{')
|
||||||
return 0;
|
return 0;
|
||||||
expr++;
|
expr++;
|
||||||
JsObject* jso = new JsObject(0,"[object Object]");
|
JsObject* jso = new JsObject(mtx,"[object Object]");
|
||||||
for (bool first = true; ; first = false) {
|
for (bool first = true; ; first = false) {
|
||||||
if (skipComments(expr) == '}') {
|
if (skipComments(expr) == '}') {
|
||||||
expr++;
|
expr++;
|
||||||
|
@ -2040,7 +2046,7 @@ JsObject* JsCode::parseObject(ParsePoint& expr, bool constOnly)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
expr++;
|
expr++;
|
||||||
bool ok = constOnly ? getSimple(expr,true) : getOperand(expr,false);
|
bool ok = constOnly ? parseSimple(expr,true,mtx) : getOperand(expr,false);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
TelEngine::destruct(jso);
|
TelEngine::destruct(jso);
|
||||||
break;
|
break;
|
||||||
|
@ -3505,13 +3511,17 @@ ScriptRun::Status JsParser::eval(const String& text, ExpOperation** result, Scri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON using native methods
|
// Parse JSON using native methods
|
||||||
JsObject* JsParser::parseJSON(const char* text)
|
ExpOperation* JsParser::parseJSON(const char* text, Mutex* mtx)
|
||||||
{
|
{
|
||||||
|
if (!text)
|
||||||
|
return 0;
|
||||||
|
ExpOperation* ret = 0;
|
||||||
JsCode* code = new JsCode;
|
JsCode* code = new JsCode;
|
||||||
ParsePoint pp(text,code);
|
ParsePoint pp(text,code);
|
||||||
JsObject* jso = code->parseObject(pp,true);
|
if (code->parseSimple(pp,true,mtx))
|
||||||
|
ret = code->popOpcode();
|
||||||
TelEngine::destruct(code);
|
TelEngine::destruct(code);
|
||||||
return jso;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a "null" object wrapper
|
// Return a "null" object wrapper
|
||||||
|
|
|
@ -2545,9 +2545,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* Parse a complete block of JSON text
|
* Parse a complete block of JSON text
|
||||||
* @param text JSON text to parse
|
* @param text JSON text to parse
|
||||||
* @return JsObject holding the content of JSON, must be dereferenced after use, NULL if parse error
|
* @param mtx Pointer to the mutex that serializes this object
|
||||||
|
* @return ExpOperation holding the content of JSON, must be dereferenced after use, NULL if parse error
|
||||||
*/
|
*/
|
||||||
static JsObject* parseJSON(const char* text);
|
static ExpOperation* parseJSON(const char* text, Mutex* mtx = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a "null" object wrapper that will identity match another "null"
|
* Get a "null" object wrapper that will identity match another "null"
|
||||||
|
|
|
@ -575,6 +575,26 @@ private:
|
||||||
Hasher* m_hasher;
|
Hasher* m_hasher;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JsJSON : public JsObject
|
||||||
|
{
|
||||||
|
YCLASS(JsJSON,JsObject)
|
||||||
|
public:
|
||||||
|
inline JsJSON(Mutex* mtx)
|
||||||
|
: JsObject("JSON",mtx,true)
|
||||||
|
{
|
||||||
|
params().addParam(new ExpFunction("parse"));
|
||||||
|
params().addParam(new ExpFunction("stringify"));
|
||||||
|
params().addParam(new ExpFunction("loadFile"));
|
||||||
|
params().addParam(new ExpFunction("saveFile"));
|
||||||
|
}
|
||||||
|
static void initialize(ScriptContext* context);
|
||||||
|
protected:
|
||||||
|
bool runNative(ObjList& stack, const ExpOperation& oper, GenObject* context);
|
||||||
|
static ExpOperation* stringify(const ExpOperation* oper, int spaces);
|
||||||
|
static void stringify(const NamedString* ns, String& buf, int spaces, int indent = 0);
|
||||||
|
static String strEscape(const char* str);
|
||||||
|
};
|
||||||
|
|
||||||
class JsChannel : public JsObject
|
class JsChannel : public JsObject
|
||||||
{
|
{
|
||||||
YCLASS(JsChannel,JsObject)
|
YCLASS(JsChannel,JsObject)
|
||||||
|
@ -2545,6 +2565,200 @@ void JsXML::initialize(ScriptContext* context)
|
||||||
addConstructor(params,"XML",new JsXML(mtx));
|
addConstructor(params,"XML",new JsXML(mtx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JsJSON::runNative(ObjList& stack, const ExpOperation& oper, GenObject* context)
|
||||||
|
{
|
||||||
|
ObjList args;
|
||||||
|
if (oper.name() == YSTRING("parse")) {
|
||||||
|
if (extractArgs(stack,oper,context,args) != 1)
|
||||||
|
return false;
|
||||||
|
ExpOperation* op = JsParser::parseJSON(static_cast<ExpOperation*>(args[0])->c_str(),mutex());
|
||||||
|
if (!op)
|
||||||
|
op = new ExpWrapper(0,"JSON");
|
||||||
|
ExpEvaluator::pushOne(stack,op);
|
||||||
|
}
|
||||||
|
else if (oper.name() == YSTRING("stringify")) {
|
||||||
|
if (extractArgs(stack,oper,context,args) < 1)
|
||||||
|
return false;
|
||||||
|
int spaces = args[2] ? static_cast<ExpOperation*>(args[2])->number() : 0;
|
||||||
|
ExpOperation* op = stringify(static_cast<ExpOperation*>(args[0]),spaces);
|
||||||
|
if (!op)
|
||||||
|
op = new ExpWrapper(0,"JSON");
|
||||||
|
ExpEvaluator::pushOne(stack,op);
|
||||||
|
}
|
||||||
|
else if (oper.name() == YSTRING("loadFile")) {
|
||||||
|
if (extractArgs(stack,oper,context,args) != 1)
|
||||||
|
return false;
|
||||||
|
ExpOperation* op = 0;
|
||||||
|
ExpOperation* file = static_cast<ExpOperation*>(args[0]);
|
||||||
|
if (!TelEngine::null(file)) {
|
||||||
|
File f;
|
||||||
|
if (f.openPath(*file)) {
|
||||||
|
int64_t len = f.length();
|
||||||
|
if (len > 0 && len <= 65536) {
|
||||||
|
DataBlock buf(0,len + 1);
|
||||||
|
char* text = (char*)buf.data();
|
||||||
|
if (f.readData(text,len) == len) {
|
||||||
|
text[len] = '\0';
|
||||||
|
op = JsParser::parseJSON(text,mutex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!op)
|
||||||
|
op = new ExpWrapper(0,"JSON");
|
||||||
|
ExpEvaluator::pushOne(stack,op);
|
||||||
|
}
|
||||||
|
else if (oper.name() == YSTRING("saveFile")) {
|
||||||
|
if (extractArgs(stack,oper,context,args) < 2)
|
||||||
|
return false;
|
||||||
|
ExpOperation* file = static_cast<ExpOperation*>(args[0]);
|
||||||
|
bool ok = !TelEngine::null(file);
|
||||||
|
if (ok) {
|
||||||
|
ok = false;
|
||||||
|
int spaces = args[2] ? static_cast<ExpOperation*>(args[2])->number() : 0;
|
||||||
|
ExpOperation* op = stringify(static_cast<ExpOperation*>(args[1]),spaces);
|
||||||
|
if (op) {
|
||||||
|
File f;
|
||||||
|
if (f.openPath(*file,true,false,true)) {
|
||||||
|
int len = op->length();
|
||||||
|
ok = f.writeData(op->c_str(),len) == len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TelEngine::destruct(op);
|
||||||
|
}
|
||||||
|
ExpEvaluator::pushOne(stack,new ExpOperation(ok));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return JsObject::runNative(stack,oper,context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpOperation* JsJSON::stringify(const ExpOperation* oper, int spaces)
|
||||||
|
{
|
||||||
|
if (!oper)
|
||||||
|
return 0;
|
||||||
|
if (spaces < 0)
|
||||||
|
spaces = 0;
|
||||||
|
else if (spaces > 10)
|
||||||
|
spaces = 10;
|
||||||
|
ExpOperation* ret = new ExpOperation("","JSON");
|
||||||
|
stringify(oper,*ret,spaces);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsJSON::stringify(const NamedString* ns, String& buf, int spaces, int indent)
|
||||||
|
{
|
||||||
|
const ExpOperation* oper = YOBJECT(ExpOperation,ns);
|
||||||
|
if (!oper) {
|
||||||
|
if (ns)
|
||||||
|
buf << strEscape(*ns);
|
||||||
|
else
|
||||||
|
buf << "null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (JsParser::isNull(*oper) || JsParser::isUndefined(*oper)) {
|
||||||
|
buf << "null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char* nl = spaces ? "\r\n" : "";
|
||||||
|
JsObject* jso = YOBJECT(JsObject,oper);
|
||||||
|
JsArray* jsa = YOBJECT(JsArray,jso);
|
||||||
|
if (jsa) {
|
||||||
|
if (jsa->length() <= 0) {
|
||||||
|
buf << "[]";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String li(' ',indent);
|
||||||
|
String ci(' ',indent + spaces);
|
||||||
|
buf << "[" << nl;
|
||||||
|
for (int32_t i = 0; ; ) {
|
||||||
|
const NamedString* p = jsa->params().getParam(String(i));
|
||||||
|
if (!p)
|
||||||
|
continue;
|
||||||
|
buf << ci;
|
||||||
|
stringify(p,buf,spaces,indent + spaces);
|
||||||
|
if (++i < jsa->length())
|
||||||
|
buf << "," << nl;
|
||||||
|
else {
|
||||||
|
buf << nl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf << li << "]";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jso) {
|
||||||
|
switch (jso->params().count()) {
|
||||||
|
case 1:
|
||||||
|
if (!jso->params().getParam(protoName()))
|
||||||
|
break;
|
||||||
|
// fall through
|
||||||
|
case 0:
|
||||||
|
buf << "{}";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjList* l = jso->params().paramList()->skipNull();
|
||||||
|
String li(' ',indent);
|
||||||
|
String ci(' ',indent + spaces);
|
||||||
|
const char* sep = spaces ? ": " : ":";
|
||||||
|
buf << "{" << nl;
|
||||||
|
while (l) {
|
||||||
|
const NamedString* p = static_cast<const NamedString*>(l->get());
|
||||||
|
l = l->skipNext();
|
||||||
|
if (p->name() == protoName())
|
||||||
|
continue;
|
||||||
|
buf << ci << strEscape(p->name()) << sep;
|
||||||
|
stringify(p,buf,spaces,indent + spaces);
|
||||||
|
p = static_cast<const NamedString*>(l->get());
|
||||||
|
if (p->name() == protoName())
|
||||||
|
l = l->skipNext();
|
||||||
|
if (l)
|
||||||
|
buf << ",";
|
||||||
|
buf << nl;
|
||||||
|
}
|
||||||
|
buf << li << "}";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oper->isBoolean())
|
||||||
|
buf << String::boolText(oper->valBoolean());
|
||||||
|
else if (oper->isNumber()) {
|
||||||
|
if (oper->isInteger())
|
||||||
|
buf << oper->number();
|
||||||
|
else
|
||||||
|
buf << "null";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
buf << strEscape(*oper);
|
||||||
|
}
|
||||||
|
|
||||||
|
String JsJSON::strEscape(const char* str)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
String s("\"");
|
||||||
|
char c;
|
||||||
|
while (str && (c = *str++)) {
|
||||||
|
if (c == '\"' || c == '\\')
|
||||||
|
s += "\\";
|
||||||
|
s += c;
|
||||||
|
}
|
||||||
|
s += "\"";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsJSON::initialize(ScriptContext* context)
|
||||||
|
{
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
Mutex* mtx = context->mutex();
|
||||||
|
Lock mylock(mtx);
|
||||||
|
NamedList& params = context->params();
|
||||||
|
if (!params.getParam(YSTRING("JSON")))
|
||||||
|
addObject(params,"JSON",new JsJSON(mtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class JsTimeEvent
|
* class JsTimeEvent
|
||||||
*/
|
*/
|
||||||
|
@ -2958,6 +3172,7 @@ bool JsAssist::init()
|
||||||
JsConfigFile::initialize(ctx);
|
JsConfigFile::initialize(ctx);
|
||||||
JsXML::initialize(ctx);
|
JsXML::initialize(ctx);
|
||||||
JsHasher::initialize(ctx);
|
JsHasher::initialize(ctx);
|
||||||
|
JsJSON::initialize(ctx);
|
||||||
if (ScriptRun::Invalid == m_runner->reset(true))
|
if (ScriptRun::Invalid == m_runner->reset(true))
|
||||||
return false;
|
return false;
|
||||||
ScriptContext* chan = YOBJECT(ScriptContext,ctx->getField(m_runner->stack(),YSTRING("Channel"),m_runner));
|
ScriptContext* chan = YOBJECT(ScriptContext,ctx->getField(m_runner->stack(),YSTRING("Channel"),m_runner));
|
||||||
|
@ -3290,6 +3505,7 @@ bool JsGlobal::runMain()
|
||||||
JsConfigFile::initialize(runner->context());
|
JsConfigFile::initialize(runner->context());
|
||||||
JsXML::initialize(runner->context());
|
JsXML::initialize(runner->context());
|
||||||
JsHasher::initialize(runner->context());
|
JsHasher::initialize(runner->context());
|
||||||
|
JsJSON::initialize(runner->context());
|
||||||
ScriptRun::Status st = runner->run();
|
ScriptRun::Status st = runner->run();
|
||||||
TelEngine::destruct(runner);
|
TelEngine::destruct(runner);
|
||||||
return (ScriptRun::Succeeded == st);
|
return (ScriptRun::Succeeded == st);
|
||||||
|
@ -3394,6 +3610,7 @@ bool JsModule::evalContext(String& retVal, const String& cmd, ScriptContext* con
|
||||||
JsConfigFile::initialize(runner->context());
|
JsConfigFile::initialize(runner->context());
|
||||||
JsXML::initialize(runner->context());
|
JsXML::initialize(runner->context());
|
||||||
JsHasher::initialize(runner->context());
|
JsHasher::initialize(runner->context());
|
||||||
|
JsJSON::initialize(runner->context());
|
||||||
}
|
}
|
||||||
ScriptRun::Status st = runner->run();
|
ScriptRun::Status st = runner->run();
|
||||||
if (st == ScriptRun::Succeeded) {
|
if (st == ScriptRun::Succeeded) {
|
||||||
|
|
Loading…
Reference in New Issue