AScript是一个开源的C#动态脚本解析执行库,支持扩展多种脚本语言。前一篇文章《AScript如何实现中文脚本引擎》中演示了扩展中文语言示例,本篇将更深入的理解AScript的多语言机制,以及如何在脚本中嵌入多种脚本语言。
一、自定义语法
AScript底层解析脚本生成token流,语法解析器对token流进行处理,就会调用ITokenHandler处理器,该接口定义如下:
1 public interface ITokenHandler
2 {
3 /// <summary>
4 /// token处理
5 /// </summary>
6 /// <param name="analyzer">语法分析器</param>
7 /// <param name="e">当前token、语法树及上下文信息</param>
8 void Build(DefaultSyntaxAnalyzer analyzer, TokenAnalyzingArgs e);
9 }
DefaultSyntaxAnalyzer参数为语法分析器,可以调用其中的BuildOneStatement或者BuildMultiStatement方法来读取一条或多条语句。
自定义语法就是对当前token以及前后语句进行处理,将结果添加到语法树,来实现期望的逻辑功能。
示例:定义sql条件语句的and语法
1 /// <summary>
2 /// <![CDATA[age>20 and age<50]]>
3 /// </summary>
4 public class AndTokenHandler : ITokenHandler
5 {
6 public static readonly AndTokenHandler Instance = new AndTokenHandler();
7
8 public void Build(DefaultSyntaxAnalyzer analyzer, TokenAnalyzingArgs e)
9 {
10 e.IsHandled = true;
11 if (!e.Ignore)
12 {
13 var op = new OperatorNode("and", DefaultSyntaxAnalyzer.OperatorPriorities["&&"], 2);
14 e.TreeBuilder.AddOperator(e.BuildContext, e.ScriptContext, e.Options, e.Control, op);
15 }
16 }
17 }
sql中的and语法等效于C#中的&&操作符,所以优先级取&&操作符的优先级,同时sql语言环境中需要注册and函数: AddFunc("and", AndAlsoOperator.Instance);
二、定义语言
AScript中提供了ScriptLang类来扩展语言,该类包含类型、变量、函数、语法解析(ITokenHandler)管理。
多语言势必会带来语法兼容问题,比如sql条件中的"="号是相等的意思,等效于C#中的"==",跟C#中的赋值符号"="有冲突,可设置属性Compatible=false表示不兼容默认语言,在脚本或ScriptContext上下文中需要指定语言才能执行。
示例:定义Sql语言(这里只实现一个简单的条件判断功能)
1 public class SqlLang : ScriptLang
2 {
3 public static readonly SqlLang Instance = new SqlLang();
4
5 public SqlLang()
6 {
7 this.Compatible = false;
8
9 AddFunc(".", DotOperator.Instance);
10 AddFunc("!", BoolNotOperator.Instance);
11 AddFunc("<", LessThanOperator.Instance);
12 AddFunc(">", GreaterThanOperator.Instance);
13 AddFunc("=", EqualOperator.Instance);
14 AddFunc(">=", GreaterThanOrEqualOperator.Instance);
15 AddFunc("<=", LessThanOrEqualOperator.Instance);
16 AddFunc("!=", NotEqualOperator.Instance);
17 AddFunc("and", AndAlsoOperator.Instance);
18 AddFunc("or", OrElseOperator.Instance);
19
20 AddTokenHandler("and", AndTokenHandler.Instance);
21 AddTokenHandler("AND", AndTokenHandler.Instance);
22 AddTokenHandler("or", OrTokenHandler.Instance);
23 AddTokenHandler("OR", OrTokenHandler.Instance);
24 AddTokenHandler("=", EqualTokenHandler.Instance);
25 AddTokenHandler("like", LikeTokenHandler.Instance);
26 AddTokenHandler("LIKE", LikeTokenHandler.Instance);
27 }
28 }
由于AScript是区分大小写的,注册token时大小写都注册一下,便于sql语句中支持大小写语法,示例中的"or"、"like"等语法代码不一一列出了,感兴趣的同学可以到开源项目的单元测试中查看,后续可能会开发一个比较完备的sql语言脚本。
注册SqlLang: Script.Langs["sql"] = SqlLang.Instance;
三、执行指定语言脚本
如果不指定语言,则使用默认且兼容的语言来执行脚本。
1、ScriptContext中设置Langs属性来指定脚本语言
1 string s = "p.Name like 'to%' or p.Age>20 and p.Age<50";
2 var script = new Script();
3 script.Context.Langs = new[] { "sql" };
4 var matchFunc = script.Compile<Person, bool>(s, "p");
5 var list = new List<Person>
6 {
7 new Person("jim", 18),
8 new Person("tony", 60),
9 new Person("tom", 19),
10 new Person("san", 25),
11 new Person("lin", 70)
12 };
13 var matchedList = list.Where(matchFunc).ToList();
14 Assert.AreEqual(3, matchedList.Count);
15 Assert.AreEqual("tony", matchedList[0].Name);
16 Assert.AreEqual("tom", matchedList[1].Name);
17 Assert.AreEqual("san", matchedList[2].Name);
2、脚本中通过#lang语法嵌入其他语言
指定多个语言使用逗号","分隔,如: #lang 中文,CSharp
1 string s = @"
2 bool isMatch(Person p) {
3 #lang sql
4 p.Age>20 and p.Age<50 or p.Name like 'to%' or p.Name='pen';
5 #end
6 }
7 var matchedList = new List<Person>();
8 foreach(var item in list) {
9 if (isMatch(item)) matchedList.Add(item);
10 }
11 matchedList;
12 ";
13 var list = new List<Person>
14 {
15 new Person("jim", 18),
16 new Person("tony", 60),
17 new Person("tom", 19),
18 new Person("san", 25)
19 };
20 var script = new Script();
21 script.Context.AddType<Person>();
22 script.Context.SetVar("list", list);
23 var matchedList = script.Eval<List<Person>>(s);
24 Assert.AreEqual(3, matchedList.Count);
25 Assert.AreEqual("tony", matchedList[0].Name);
26 Assert.AreEqual("tom", matchedList[1].Name);
27 Assert.AreEqual("san", matchedList[2].Name);
四、结语
AScript 的多语言扩展能力,在同一脚本中嵌入不同脚本语言,让开发者和用户可以使用最合适的语言进行脚本配置,提高配置灵活性和产品体验。
AScript开源地址:https://gitee.com/rockey627/AScript