nlib
exi/script/script.cpp

Defines a simplified language using XML. You can implement an XML-based programming language that obviates the need for both lexical analysis and syntactic analysis.

The sample implements a simple script language that can only define and assign int-type variables, call embedded functions, and execute while statements. Using XML makes it possible to define a programming language without implementing lexing and parsing. Also, using binary XML means that the size of the script does not increase even though XML is being used.

An XML-based programming language is indeed verbose, but the use of binary XML prevents the script from getting too big even though XML is being used.

/*--------------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. They may not be disclosed to third
parties or copied or duplicated in any form, in whole or in part, without the
prior written consent of Nintendo.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
#include <map>
#include <string>
#include <vector>
using nlib_ns::exi::XmlStreamWriter;
using nlib_ns::exi::XmlStreamReader;
using nlib_ns::exi::ExiAllocator;
#define N(x) NLIB_EXI_LITERAL(x)
#define M(x) NLIB_EXI_UTF8(x)
typedef std::basic_string< ::nlib_ns::exi::ExiChar> StdString;
const int kBufSize = 1024 * 128;
unsigned char g_buf[kBufSize];
const int kDataBufSize = 1024;
unsigned char g_data_buf[kDataBufSize];
bool SetupScriptXml() {
MemoryOutputStream os(g_data_buf, kDataBufSize);
UniquePtr<XmlStreamWriter> w(XmlStreamWriter::Create(&os));
if (!w.get()) return false;
// In pseudo code:
//
// var result=0;
// var i=0;
// while(i<=max) {
// result=result+i;
// i=i+1;
// }
//
// In XML:
//
// <var name="result" value="0"/>
// <var name="i" value="0"/>
// <while>
// <leq><i/><max/></leq>
// <add><result/><result/><i/></add>
// <add><i/><i/><value value="1"/></add>
// </while>
w->WriteStartDocument();
w->WriteStartElement(N("program"));
w->WriteStartElement(N("var"));
w->WriteAttribute(N("name"), N("result"));
w->WriteAttribute(N("value"), N("0"));
w->WriteEndElement();
w->WriteStartElement(N("var"));
w->WriteAttribute(N("name"), N("i"));
w->WriteAttribute(N("value"), N("0"));
w->WriteEndElement();
w->WriteStartElement(N("while"));
w->WriteStartElement(N("leq"));
w->WriteEmptyElement(N("i"));
w->WriteEmptyElement(N("max"));
w->WriteEndElement();
w->WriteStartElement(N("add"));
w->WriteEmptyElement(N("result"));
w->WriteEmptyElement(N("result"));
w->WriteEmptyElement(N("i"));
w->WriteEndElement();
w->WriteStartElement(N("add"));
w->WriteEmptyElement(N("i"));
w->WriteEmptyElement(N("i"));
w->WriteStartElement(N("value"));
w->WriteAttribute(N("value"), N("1"));
w->WriteEndElement();
w->WriteEndElement();
w->WriteEndElement();
w->WriteEndElement();
w->WriteEndDocument();
w->Close();
nlib_printf("program size = %" PRIuS " bytes\n", os.Pos());
return !!*w;
}
typedef std::map<StdString, int> Variables;
Variables g_variables;
int GetValue(const StdString& nameOrVal) {
// Returns integer values for a variable or a literal value.
Variables::const_iterator it;
if ((it = g_variables.find(nameOrVal)) != g_variables.end()) return it->second;
int rval;
errno_t e = nlib_strto_int32(&rval, M(nameOrVal.c_str()), NULL, 10);
if (nlib_is_error(e)) rval = 0;
return rval;
}
struct FuncStruct {
typedef std::vector<StdString> Args;
typedef int (*Func)(const Args&);
typedef std::map<StdString, Func> Functions;
Func func;
Args args;
int Call() { return func(args); }
bool Setup(XmlStreamReader* r, bool skip_next = false);
static int Leq(const Args& args) {
int lhs = GetValue(args[0]);
int rhs = GetValue(args[1]);
int result = lhs <= rhs ? 1 : 0;
g_variables[N("_")] = result;
return result;
}
static int Add(const Args& args) {
int result = 0;
size_t n = args.size();
for (size_t i = 1; i < n; ++i) result += GetValue(args[i]);
g_variables[args[0]] = result;
g_variables[N("_")] = result;
return result;
}
static Functions functions;
static void Init() {
// Registers built-in functions.
functions[N("leq")] = &Leq;
functions[N("add")] = &Add;
}
};
FuncStruct::Functions FuncStruct::functions;
bool FuncStruct::Setup(XmlStreamReader* r, bool skipNext) {
// <function name>
// <variable name/><!-- variable argument -->
// <value value="..."/><!-- literal argument -->
// ....
// </function name>
if (!skipNext) {
if (!r->HasNext() || r->Next() != XmlStreamReader::START_ELEMENT) return false;
}
const ExiChar* funcname = r->GetLocalName();
Functions::const_iterator it;
if ((it = functions.find(funcname)) == functions.end()) return false;
func = it->second;
int depth = 0;
do {
if (!r->HasNext()) return false;
switch (r->Next()) {
case XmlStreamReader::START_ELEMENT:
if (StrCmp(N("value"), r->GetLocalName()) == 0) {
// <value value="int value"/>
args.push_back(r->GetAttributeValue(N(""), N("value")));
} else {
// <varname/>
args.push_back(r->GetLocalName());
}
++depth;
break;
case XmlStreamReader::END_ELEMENT:
--depth;
break;
default:
break;
}
} while (depth >= 0);
return true;
}
bool SampleExec() {
if (!SetupScriptXml()) return false;
ExiAllocator::Reset();
// Defines built-in functions.
FuncStruct::Init();
// Predefined variable
g_variables[N("max")] = 10;
MemoryInputStream is(g_data_buf, kDataBufSize);
UniquePtr<XmlStreamReader> r(XmlStreamReader::Create(&is));
if (!r.get()) return false;
while (r->HasNext()) {
switch (r->Next()) {
case XmlStreamReader::START_ELEMENT: {
const ExiChar* ln = r->GetLocalName();
if (StrCmp(ln, N("var")) == 0) {
// <var name="varname" value="int value"/>
int result = GetValue(r->GetAttributeValue(N(""), N("value")));
g_variables[r->GetAttributeValue(N(""), N("name"))] = result;
g_variables[N("_")] = result;
} else if (StrCmp(ln, N("while")) == 0) {
FuncStruct cond;
std::vector<FuncStruct> execVec;
if (!cond.Setup(r.get())) return false;
if (!r->HasNext()) return false;
while (r->Next() == XmlStreamReader::START_ELEMENT) {
FuncStruct item;
if (!item.Setup(r.get(), true)) return false;
execVec.push_back(item);
}
while (cond.Call()) {
size_t n = execVec.size();
for (size_t i = 0; i < n; ++i) execVec[i].Call();
}
} else if (StrCmp(ln, N("program")) != 0) {
FuncStruct funcStruct;
bool result = funcStruct.Setup(r.get());
if (!result) return false;
funcStruct.Call();
// g_variable[L"_"] stores the result.
}
} break;
default:
break;
}
}
// The result is stored in the variable named "result".
nlib_printf("result = %d\n", g_variables[N("result")]);
return true;
}
bool SampleMain(int, char**) {
// Initializes the allocator for the XML parser, settings the memory for it.
if (ExiAllocator::Init(g_buf, kBufSize) != 0) {
nlib_printf("ExiAllocator::Initialize failed\n");
return false;
}
bool rval = SampleExec();
// Deallotes the memory for the XML parser. Returns g_buf.
ExiAllocator::Finalize();
return rval;
}
NLIB_MAINFUNC