nlib
exi/script/script.cpp

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

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

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

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