很多人都知道帕雷托法则吧,就是那个80%和20%什么什么的法则。帕雷托法则适用于多种领域。游戏编程中的一些类库,比如脚本库的应用,我所体会到的情况是Lua或Python之流中的大部分特性都很少用到,即使有用也不是非用不可,gameplay脚本逻辑中最常见的是一般的脚本接口调用和条件分支处理,什么循环、复杂表达式求值、容器、类等等通通可以略去。不妨这样描述:脚本驱动的gameplay中80%的逻辑由主流通用脚本语言中20%的功能特性完成。那么对于绝大多数gameplay脚本应用本就没必要大动干戈的集成大坨的完整脚本语言进来,也没必要花大力气自主研发通用脚本库,只需对各自的需求有针对性的定制一个小巧的领域特定脚本语言即可直接满足绝大部分需求,不好满足的通过实现一定的扩展也能达到目的。出于这个目的,周末我抽了几个小时的功夫写了一个极小的脚本语言,叫做XPL,小到只有几百行代码,只需一个头文件就能容纳。我是不喜欢在博客里贴大段甚至整篇代码的,但是这次的足够小巧,最好的表达就是让代码自己说明自己了,就破一次例把所有代码贴出来。还可以从http://code.google.com/p/xpl/source/browse/#svn%2Ftrunk获取最新的代码。开源协议使用DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE。
xpl.h:
/** * Author: Tony, [email protected] * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ #ifndef __XPL_H__ #define __XPL_H__ #ifdef _MSC_VER # pragma warning(disable : 4201) # pragma warning(disable : 4706) #endif /* _MSC_VER */ #include <memory.h> #include <assert.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* ** {======================================================== ** Macros and typedefines */ #ifndef XPLINTERNAL # define XPLINTERNAL static #endif /* XPLINTERNAL */ #ifndef XPLAPI # define XPLAPI static #endif /* XPLAPI */ #ifndef NULL # define NULL 0 #endif /* NULL */ #ifndef _countof # define _countof(a) (sizeof(a) / sizeof(*(a))) #endif /* _countof */ #ifndef xpl_assert # define xpl_assert(e) assert(e) #endif /* xpl_assert */ /** * @brief XPL scripting programming interface registering macros * @note The interfaces are storaged in a common array, you could put * these macros at global or local scopes to make your customized * interfaces. */ #ifndef XPL_FUNC_REGISTER # define XPL_FUNC_REGISTER /**< Begin an interface declaration */ # define XPL_FUNC_BEGIN(a) \ static xpl_func_info_t a[] = { \ { "if", _xpl_core_if }, \ { "then", _xpl_core_then }, \ { "elseif", _xpl_core_elseif }, \ { "else", _xpl_core_else }, \ { "endif", _xpl_core_endif }, \ { "or", _xpl_core_or }, \ { "and", _xpl_core_and }, \ { "yield", _xpl_core_yield }, /**< Declare an interface */ # define XPL_FUNC_ADD(n, f) \ { n, f }, /**< End an interface declaration */ # define XPL_FUNC_END \ { NULL, NULL }, \ }; #endif /* XPL_FUNC_REGISTER */ /** * @brief Skip meaningless parts, like comment and blank. */ #ifndef SKIP_MEANINGLESS # define SKIP_MEANINGLESS(s) _xpl_trim(&(s)->cursor); if(xpl_skip_comment(s) == XS_OK) _xpl_trim(&(s)->cursor) #endif /* SKIP_MEANINGLESS */ /** * @brief XPL function execution status */ typedef enum xpl_status_t { XS_OK, /**< Totally OK */ XS_SUSPENT, /**< Suspent */ XS_ERR, /**< Some error occured */ XS_NO_ENOUGH_BUFFER_SIZE, /**< String buffer too small */ XS_NO_COMMENT, /**< No comment found */ XS_NO_PARAM, /**< No param found */ XS_PARAM_TYPE_ERROR, /**< Parameter convertion failed */ XS_COUNT, } xpl_status_t; /** * @brief Boolean value composing type */ typedef enum xpl_bool_composing_t { XBC_NIL, /**< Assign new value directly */ XBC_OR, /**< Old value OR new value */ XBC_AND, /**< Old value AND new value */ } xpl_bool_composing_t; struct xpl_context_t; /** * @brief XPL scripting programming interface signature * * @param[in] _s - XPL context */ typedef xpl_status_t (* xpl_func_t)(struct xpl_context_t* _s); /** * @brief XPL scripting programming interface information */ typedef struct xpl_func_info_t { const char* name; /**< Interface name */ xpl_func_t func; /**< Pointer to interface function */ } xpl_func_info_t; /** * @brief XPL context structure */ typedef struct xpl_context_t { /** * @brief Registered interfaces */ struct { xpl_func_info_t* funcs; /**< Pointer to array of registered interfaces */ int funcs_count; /**< Count of registered interfaces */ }; /** * @brief Script source code indicator */ struct { const char* text; /**< Script source text */ const char* cursor; /**< Script execution cursor */ }; /** * @brief Boolean value */ struct { xpl_bool_composing_t bool_composing; /**< Boolean value composing type */ int bool_value; /**< Current boolean value */ }; void* userdata; /**< Pointer to user defined data */ } xpl_context_t; /* ========================================================} */ /* ** {======================================================== ** Function declarations */ /** * @brief Opens an XPL context * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_open(xpl_context_t* _s, xpl_func_info_t* _f); /** * @brief Closes an XPL context * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_close(xpl_context_t* _s); /** * @brief Loads a script * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_load(xpl_context_t* _s, const char* _t); /** * @brief Reloads current script, set execution cursor to begin point. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_reload(xpl_context_t* _s); /** * @brief Unloads a script * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_unload(xpl_context_t* _s); /** * @brief Runs a script * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_run(xpl_context_t* _s); /** * @brief Tries to peek one function * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_peek_func(xpl_context_t* _s, xpl_func_info_t** _f); /** * @brief Runs a single step * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_step(xpl_context_t* _s); /** * @brief Skips a piece of comment * * @param[in] _s - XPL context * @return - Returns execution status */ XPLAPI xpl_status_t xpl_skip_comment(xpl_context_t* _s); /** * @brief Determines whether current XPL context contains a parameter * * @param[in] _s - XPL context * @return - Returns execution status, XS_OK if has param, or XS_NO_PARAM if does not. */ XPLAPI xpl_status_t xpl_has_param(xpl_context_t* _s); /** * @brief Pops a integer parameter from XPL context * * @param[in] _s - XPL context * @param[out] _o - Destination buffer * @return - Returns execution status */ XPLAPI xpl_status_t xpl_pop_int(xpl_context_t* _s, int* _o); /** * @brief Pops a float parameter from XPL context * * @param[in] _s - XPL context * @param[out] _o - Destination buffer * @return - Returns execution status */ XPLAPI xpl_status_t xpl_pop_float(xpl_context_t* _s, float* _o); /** * @brief Pops a string parameter from XPL context * * @param[in] _s - XPL context * @param[out] _o - Destination buffer * @param[in] _l - Destination buffer size * @return - Returns execution status */ XPLAPI xpl_status_t xpl_pop_string(xpl_context_t* _s, char* _o, int _l); /** * @brief Pushes a boolean value to XPL context * * @param[in] _s - XPL context * @param[in] _b - Boolean value * @return - Returns execution status */ XPLAPI xpl_status_t xpl_push_bool(xpl_context_t* _s, int _b); /** * @brief Scripting programming interface: * 'if' statement, dummy function. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_if(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'then' statement, main logic about 'if-then-elseif-else-endif'. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_then(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'elseif' statement, dummy function. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_elseif(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'else' statement, dummy function. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_else(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'endif' statement, dummy function. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_endif(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'or' statement, set current boolean composing type as OR. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_or(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'and' statement, set current boolean composing type as AND. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_and(xpl_context_t* _s); /** * @brief Scripting programming interface: * 'yield' statement, suspend current execution. * * @param[in] _s - XPL context * @return - Returns execution status */ XPLINTERNAL xpl_status_t _xpl_core_yield(xpl_context_t* _s); /** * @brief Determines whether a char is a single quote * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_squote(unsigned char _c); /** * @brief Determines whether a char is a double quote * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_dquote(unsigned char _c); /** * @brief Determines whether a char is a comma * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_comma(unsigned char _c); /** * @brief Determines whether a char is an exclamation * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_exclamation(unsigned char _c); /** * @brief Determines whether a char is a colon * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_colon(unsigned char _c); /** * @brief Determines whether a char is a blank * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_blank(unsigned char _c); /** * @brief Determines whether a char is a seperator * * @param[in] _c - Char to be determined * @return Returns non-zero if matching */ XPLINTERNAL int _xpl_is_seperator(unsigned char _c); /** * @brief Trims extra chars * * @param[in/out] - String to be trimmed * @return - Returns count of trimmed chars */ XPLINTERNAL int _xpl_trim(const char** _c); /** * @brief Compires two strings * * @param[in] _s - Source string * @param[in] _d - Destination buffer * @return - Returns 1 if _s > _d, -1 if _s < _d, 0 if _s = _d */ XPLINTERNAL int _xpl_strcmp(const char* _s, const char* _d); /** * @brief Copies source string to destination * * @param[out] _d - Destination buffer * @param[in] _s - Source string * @param[in] _l - Size of destination buffer * @return - Returns count of copied chars */ XPLINTERNAL int _xpl_strcpy(char* _d, const char* _s, int _l); /** * @brief Compires scripting programming interface information in quick sorting * * @param[in] _l - First interface information * @param[in] _r - Second interface information * @return - Returns 1 if _l > _r, -1 if _l < _r, 0 if _l = _r */ XPLINTERNAL int _xpl_func_info_srt_cmp(const void* _l, const void* _r); /** * @brief Compires scripting programming interface information in binary searching * * @param[in] _k - Searching key * @param[in] _i - Interface information * @return - Returns 1 if _k > _i, -1 if _k < _i, 0 if _k = _i */ XPLINTERNAL int _xpl_func_info_sch_cmp(const void* _k, const void* _i); /* ========================================================} */ /* ** {======================================================== ** Function definitions */ XPLAPI xpl_status_t xpl_open(xpl_context_t* _s, xpl_func_info_t* _f) { xpl_assert(_s && _f); memset(_s, 0, sizeof(xpl_context_t)); _s->funcs = _f; while(_f[_s->funcs_count].name && _f[_s->funcs_count].func) _s->funcs_count++; qsort(_f, _s->funcs_count, sizeof(xpl_func_info_t), _xpl_func_info_srt_cmp); return XS_OK; } XPLAPI xpl_status_t xpl_close(xpl_context_t* _s) { xpl_assert(_s); memset(_s, 0, sizeof(xpl_context_t)); return XS_OK; } XPLAPI xpl_status_t xpl_load(xpl_context_t* _s, const char* _t) { xpl_assert(_s && _t); if(_s->text) xpl_unload(_s); _s->cursor = _s->text = _t; return XS_OK; } XPLAPI xpl_status_t xpl_reload(xpl_context_t* _s) { xpl_assert(_s && _s->text); _s->cursor = _s->text; return XS_OK; } XPLAPI xpl_status_t xpl_unload(xpl_context_t* _s) { xpl_assert(_s); _s->cursor = _s->text = NULL; return XS_OK; } XPLAPI xpl_status_t xpl_run(xpl_context_t* _s) { xpl_status_t ret = XS_OK; xpl_assert(_s && _s->text); while(*_s->cursor && ret == XS_OK) ret = xpl_step(_s); return ret; } XPLAPI xpl_status_t xpl_peek_func(xpl_context_t* _s, xpl_func_info_t** _f) { xpl_status_t ret = XS_OK; xpl_func_info_t* func = NULL; xpl_assert(_s && _s->text && _f); SKIP_MEANINGLESS(_s); if(_xpl_is_comma(*(unsigned char*)_s->cursor)) { _s->cursor++; } else { func = bsearch(_s->cursor, _s->funcs, _s->funcs_count, sizeof(xpl_func_info_t), _xpl_func_info_sch_cmp); if(!func) return XS_ERR; *_f = func; } return ret; } XPLAPI xpl_status_t xpl_step(xpl_context_t* _s) { xpl_status_t ret = XS_OK; xpl_func_info_t* func = NULL; xpl_assert(_s && _s->text); if(ret = xpl_peek_func(_s, &func) != XS_OK) return ret; if(!func) return ret; _s->cursor += strlen(func->name); SKIP_MEANINGLESS(_s); if(ret = func->func(_s) != XS_OK) return ret; return ret; } XPLAPI xpl_status_t xpl_skip_comment(xpl_context_t* _s) { xpl_assert(_s && _s->text); if(_xpl_is_squote(*(unsigned char*)_s->cursor)) { do { _s->cursor++; } while(*(unsigned char*)_s->cursor != '\0' && !_xpl_is_squote(*(unsigned char*)_s->cursor)); _s->cursor++; return XS_OK; } return XS_NO_COMMENT; } XPLAPI xpl_status_t xpl_has_param(xpl_context_t* _s) { xpl_func_info_t* func = NULL; xpl_assert(_s && _s->text); SKIP_MEANINGLESS(_s); xpl_peek_func(_s, &func); return ((_xpl_is_comma(*(unsigned char*)_s->cursor) || func) ? XS_NO_PARAM : XS_OK); } XPLAPI xpl_status_t xpl_pop_int(xpl_context_t* _s, int* _o) { xpl_status_t ret = XS_OK; char* conv_suc = NULL; char buf[32] = { '\0' }; xpl_assert(_s && _s->text && _o); if(ret = xpl_pop_string(_s, buf, sizeof(buf)) != XS_OK) return ret; *_o = (int)strtol(buf, &conv_suc, 0); if(*conv_suc != '\0') ret = XS_PARAM_TYPE_ERROR; return ret; } XPLAPI xpl_status_t xpl_pop_float(xpl_context_t* _s, float* _o) { xpl_status_t ret = XS_OK; char* conv_suc = NULL; char buf[32] = { '\0' }; xpl_assert(_s && _s->text && _o); if(ret = xpl_pop_string(_s, buf, sizeof(buf)) != XS_OK) return ret; *_o = (float)strtod(buf, &conv_suc); if(*conv_suc != '\0') ret = XS_PARAM_TYPE_ERROR; return ret; } XPLAPI xpl_status_t xpl_pop_string(xpl_context_t* _s, char* _o, int _l) { const char* src = NULL; char* dst = NULL; xpl_assert(_s && _s->text && _o); src = _s->cursor; dst = _o; if(_xpl_is_dquote(*(unsigned char *)src)) { src++; while(!_xpl_is_dquote(*(unsigned char *)src)) { *dst++ = *src++; if(dst + 1 - _o > _l) return XS_NO_ENOUGH_BUFFER_SIZE; } src++; } else { while(!_xpl_is_seperator(*(unsigned char *)src)) { *dst++ = *src++; if(dst + 1 - _o > _l) return XS_NO_ENOUGH_BUFFER_SIZE; } } _s->cursor = src; return XS_OK; } XPLAPI xpl_status_t xpl_push_bool(xpl_context_t* _s, int _b) { xpl_assert(_s && _s->text); switch(_s->bool_composing) { case XBC_NIL: _s->bool_value = !!_b; break; case XBC_OR: _s->bool_value |= !!_b; break; case XBC_AND: _s->bool_value &= !!_b; break; default: xpl_assert(!"Unknow boolean composing type"); break; } return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_if(xpl_context_t* _s) { xpl_assert(_s && _s->text); return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_then(xpl_context_t* _s) { xpl_status_t ret = XS_OK; xpl_func_info_t* func = NULL; xpl_assert(_s && _s->text); if(_s->bool_value) { _s->bool_value = 0; _s->bool_composing = XBC_NIL; do { if(ret = xpl_peek_func(_s, &func) != XS_OK) return ret; if(!func) continue; if(func->func == _xpl_core_elseif || func->func == _xpl_core_else || func->func == _xpl_core_endif) break; _s->cursor += strlen(func->name); SKIP_MEANINGLESS(_s); if(ret = func->func(_s) != XS_OK) return ret; } while(*_s->cursor); do { if(ret = xpl_peek_func(_s, &func) != XS_OK) return ret; if(!func) continue; _s->cursor += strlen(func->name); if(func->func == _xpl_core_endif) break; } while(*_s->cursor); } else { _s->bool_value = 0; _s->bool_composing = XBC_NIL; do { xpl_peek_func(_s, &func); if(!func) continue; if(func->func == _xpl_core_elseif || func->func == _xpl_core_else || func->func == _xpl_core_endif) break; _s->cursor += strlen(func->name); } while(*_s->cursor); } return ret; } XPLINTERNAL xpl_status_t _xpl_core_elseif(xpl_context_t* _s) { xpl_assert(_s && _s->text); return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_else(xpl_context_t* _s) { xpl_assert(_s && _s->text); return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_endif(xpl_context_t* _s) { xpl_assert(_s && _s->text); return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_or(xpl_context_t* _s) { xpl_assert(_s && _s->text); _s->bool_composing = XBC_OR; return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_and(xpl_context_t* _s) { xpl_assert(_s && _s->text); _s->bool_composing = XBC_AND; return XS_OK; } XPLINTERNAL xpl_status_t _xpl_core_yield(xpl_context_t* _s) { xpl_assert(_s && _s->text); return XS_SUSPENT; } XPLINTERNAL int _xpl_is_squote(unsigned char _c) { return _c == '\''; } XPLINTERNAL int _xpl_is_dquote(unsigned char _c) { return _c == '"'; } XPLINTERNAL int _xpl_is_comma(unsigned char _c) { return _c == ','; } XPLINTERNAL int _xpl_is_exclamation(unsigned char _c) { return _c == '!'; } XPLINTERNAL int _xpl_is_colon(unsigned char _c) { return _c == ':'; } XPLINTERNAL int _xpl_is_blank(unsigned char _c) { return _c == ' ' || _c == '\t' || _c == '\r' || _c == '\n'; } XPLINTERNAL int _xpl_is_seperator(unsigned char _c) { return _xpl_is_blank(_c) || _xpl_is_comma(_c) || _xpl_is_exclamation(_c) || _xpl_is_colon(_c) || _xpl_is_squote(_c) || _xpl_is_dquote(_c); } XPLINTERNAL int _xpl_trim(const char** _c) { int ret = 0; xpl_assert(_c && *_c); while(_xpl_is_blank(**_c)) { ret++; (*_c)++; } return ret; } XPLINTERNAL int _xpl_strcmp(const char* _s, const char* _d) { int ret = 0; while(!(ret = (_xpl_is_seperator(*(unsigned char *)_s) ? '\0' : *(unsigned char *)_s) - *(unsigned char *)_d) && *_d) { _s++; _d++; } if(ret < 0) ret = -1; else if(ret > 0) ret = 1; return ret; } XPLINTERNAL int _xpl_strcpy(char* _d, const char* _s, int _l) { int ret = 0; char* dst = _d; while(*dst++ = *_s++) { if(dst + 1 - _d > _l) return -1; else ret++; } return ret; } XPLINTERNAL int _xpl_func_info_srt_cmp(const void* _l, const void* _r) { xpl_func_info_t* l = (xpl_func_info_t*)_l; xpl_func_info_t* r = (xpl_func_info_t*)_r; xpl_assert(l && r); return _xpl_strcmp(l->name, r->name); } XPLINTERNAL int _xpl_func_info_sch_cmp(const void* _k, const void* _i) { const char* k = (const char*)_k; xpl_func_info_t* i = (xpl_func_info_t*)_i; xpl_assert(k && i); return _xpl_strcmp(k, i->name); } /* ========================================================} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __XPL_H__ */
代码没有用什么高深的技术,都是非常简单C语言的基础。实现用到最复杂的数据结构是数组,没有在heap上分配空间,甚至只需要一个简单的上下文结构体记录一些必要信息,执行流程只是对源字符串的遍历处理,内存开销甚小,CPU效率大抵和strlen相当。下面再贴出一个使用例子。
test.c:
/** * Author: Tony, [email protected] * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ #include "xpl.h" xpl_status_t test1(xpl_context_t* _s) { float f = 0.0f; printf("test1\n"); if(xpl_has_param(_s) == XS_OK) { xpl_pop_float(_s, &f); printf("has_param %f\n", f); } return XS_OK; } xpl_status_t test2(xpl_context_t* _s) { char buf[64] = { '\0' }; printf("test2\n"); if(xpl_has_param(_s) == XS_OK) { xpl_pop_string(_s, buf, 64); printf("has_param %s\n", buf); } return XS_OK; } xpl_status_t test3(xpl_context_t* _s) { printf("test3\n"); return XS_OK; } xpl_status_t cond1(xpl_context_t* _s) { printf("cond1\n"); xpl_push_bool(_s, 0); return XS_OK; } xpl_status_t cond2(xpl_context_t* _s) { printf("cond2\n"); xpl_push_bool(_s, 1); return XS_OK; } static xpl_context_t xpl; int main() { XPL_FUNC_BEGIN(funcs) XPL_FUNC_ADD("test3", test3) XPL_FUNC_ADD("test2", test2) XPL_FUNC_ADD("test1", test1) XPL_FUNC_ADD("cond2", cond2) XPL_FUNC_ADD("cond1", cond1) XPL_FUNC_END xpl_open(&xpl, funcs); xpl_load(&xpl, "if cond1 then test1 3.14 elseif cond2 then test2 \"hello world\" else test3 endif"); xpl_run(&xpl); xpl_unload(&xpl); xpl_close(&xpl); return 0; }
语言标准核心只支持if-then-elseif-else-endif、yield、注释、脚本接口调用几个特性,但你可以通过扩展的方式获得数组、容器、循环、表达式计算等特性;脚本接口的添加也像在数组中添加一个元素一样简单。
XPL不是最丰富、强大、全面的脚本语言,但我希望它是最易扩展、调试和划算的。
注:本文由本人自己从http://blog.sina.com.cn/s/blog_5e6fd4290100yitj.html投影至此。