nlib
exi/script/script.cpp

XMLを用いて簡易言語を定義しています。 XMLベースでプログラミング言語を作成すれば字句解析と構文解析を省略してプログラミング言語を実装することができます。

サンプルでは、int型の変数の定義及び代入・組み込み関数の呼び出し・while文のみが可能な単純なスクリプト言語を実装しています。 XMLを利用することにより字句解析・構文解析を実装しなくてもプログラミング言語を定義することができます。 また、バイナリXMLを利用することによりXMLを利用しているにも関わらずそれほどスクリプトのサイズが増えていません。

XMLベースのプログラミング言語は冗長な記述になるのですが、バイナリXMLを利用することによりXMLを利用しているにも関わらずそれほどスクリプトのデータサイズが増えていません。

/*---------------------------------------------------------------------------*
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