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)2012-2016 Nintendo. All rights reserved.
These coded instructions, statements, and computer programs contain
proprietary information of Nintendo of America Inc. and/or Nintendo
Company Ltd., and are protected by Federal copyright law. 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.
*---------------------------------------------------------------------------*/
#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 BUF_SIZE = 1024 * 128;
unsigned char g_Buf[BUF_SIZE];
const int DATABUF_SIZE = 1024;
unsigned char g_DataBuf[DATABUF_SIZE];
bool SetupScriptXml() {
MemoryOutputStream os(g_DataBuf, DATABUF_SIZE);
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 skipNext = 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_DataBuf, DATABUF_SIZE);
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, BUF_SIZE) != 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