CSLight 是业内比较厉害的一个人(李剑英)写出来用于对U3D脚本进行热更新的开源项目,从他的一系列博客文章可以看出,他是一个很有想法的人,尤其是在U3D脚本热更新和资源热加载这块有自己的观点。相比ulua我个人还是倾向于CSLight毕竟一个项目里面存在好几种语法规则在维护上是一件很麻烦的事,这两种脚本有人做过测评效率上差不多。
1.脚本:
什么是脚本?它的体现形式是一段字符串,然后被一个宿主环境解释执行,例如js在v8里面解释执行,所以js对于v8来说就是脚本。广义上来说,所有的语言都是脚本,而它们的宿主环境就是计算机硬件。无论是解释执行还是编译执行,对于它们的宿主环境来说,都需要做两件事情,解释和执行。解释,就是把一段信息翻译成宿主环境能够认识的东西。
2.CSLight:
CSLight就是一个宿主环境,而它的脚本是一个C#规范子集的一段字符串,这个子集的范围在作者博客有介绍:http://www.cnblogs.com/crazylights/p/3888932.html。脚本在解释之后,会把信息以一种宿主环境可以直接调用的形式存储,而CSLight会把这些信息存储在CLS_Environment里面。
3.CLS_Environment:
(1)构造:
Dictionary<CLType, ICLS_Type> types = new Dictionary<CLType, ICLS_Type>();
Dictionary<string, ICLS_Type> typess = new Dictionary<string, ICLS_Type>();
Dictionary<string, ICLS_Function> calls = new Dictionary<string, ICLS_Function>();
public CLS_Environment(ICLS_Logger logger)
{
//if(useNamespace==true)
//{
// throw new Exception("使用命名空间还不能完全兼容,建议关闭");
//}
this.logger = logger;
//this.useNamespace = useNamespace;
tokenParser = new CLS_TokenParser();
compiler = new CLS_Expression_Compiler(logger);
RegType(new CLS_Type_Int());
RegType(new CLS_Type_UInt());
RegType(new CLS_Type_Float());
RegType(new CLS_Type_Double());
RegType(new CLS_Type_String());
RegType(new CLS_Type_Var());
RegType(new CLS_Type_Bool());
RegType(new CLS_Type_Lambda());
RegType(new CLS_Type_Delegate());
RegType(new CLS_Type_Byte());
RegType(new CLS_Type_Char());
RegType(new CLS_Type_UShort());
RegType(new CLS_Type_Sbyte());
RegType(new CLS_Type_Short());
RegType(new CLS_Type_Long());
typess["null"] = new CLS_Type_NULL();
//contentGloabl = CreateContent();
//if (!useNamespace)//命名空间模式不能直接用函数
{
RegFunction(new FunctionTrace());
}
}
从构造上来看,这里主要是注册类型和方法信息,而这些类型和方法都是会被脚本用到的,也就是说如果types内容为null脚本写的再漂亮CSLight也是不认识的。
其次需要主要的是构造必须传入一个ICLS_Logger 对象用来输出日志,在运行时输出错误日志是必须的,所以这里是强制要求有一个log对象。而这种方式也很有效的避免了与U3D的依赖。
CLS_TokenParser和CLS_Expression_Compiler应该就是用来执行脚本的。
(2)相对重要的方法:
public void RegType(ICLS_Type type)
public void RegFunction(ICLS_Function func)
public CLS_Content CreateContent()
public void Project_Compiler(Dictionary<string, IList<Token>> project, bool embDebugToken)
public CLS_Content.Value Expr_Execute(ICLS_Expression expr, CLS_Content content = null)
{
if (content == null) content = CreateContent();
return expr.ComputeValue(content);
}
public IList<Token> ParserToken(string code)
{
return tokenParser.Parse(code);
}
public ICLS_Expression Expr_CompilerToken(IList<Token> listToken, bool SimpleExpression = false)
{
return SimpleExpression ? compiler.Compiler_NoBlock(listToken, this) : compiler.Compiler(listToken, this);
}
Project_Compiler:这里的工程可以理解为文件夹,它的功能是把一个文件夹下面所有cs文件进行解释,然后把里面的类方法注册到CLS_Environment对象里面。
CLS_Content:可以把它理解为一个容器,存储结果或者参数。
3.常用接口:
(1)CLS_Content:
public class Value
{
public CLType type;
public object value;
public int breakBlock = 0;//是否是块结束
public static Value FromICLS_Value(ICLS_Value value)
{
Value v = new Value();
v.type = value.type;
v.value = value.value;
return v;
}
}
public Dictionary<string, Value> values = new Dictionary<string, Value>();
public void Define(string name,CLType type)
public void Set(string name,object value)
public void DefineAndSet(string name,CLType type,object value)
public Value Get(string name)
在CLS_Content的内部定义了一个Value类型作为它的值的类型。这里需要注意的是CLS_Content需要先知道object的CLType,然后才能存储它的值。
(2)ICLS_Expression:表达式,一个复杂的表达式由多个简单的组成,这个接口计算表达式的结果。
ICLS_Value:继承ICLS_Expression接口,在其基础上增加了CLType类型信息。
ICLS_Environment_Compiler:编译整个环境
ICLS_Expression_Compiler:编译表达式
这里的编译我个人的理解就是把解析好的指令执行一遍,但是好像又不对,应该是属于解析的第二个过程,第一个过程是分析字符串,把字符串分析成一段段的表达式。这个过程是把表达式换成CSLight可识别的形式。
(3)ICLS_Function和ICLS_Function_Member:接口很简单只有一个Call方法
CLS_Content.Value Call(CLS_Content content, IList<CLS_Content.Value> param);
CLS_Content.Value Call(CLS_Content content, object objthis, IList<CLS_Content.Value> param);
上面的是全局方法,下面的是成员函数,多了一个this指针。
(4)ICLS_Logger:打印日志
总结:大致看了一下源代码,多是一些解释执行的东西,大致有点了解就行了,毕竟用好才是最重要的。