脚本语言:Xmas(三)

时间:2021-02-09 07:30:06

  自从将Xmas的GC换成现在的非迁移式的全局收集器后,最近几个月一直耗在Xmas上面;最明显的改变就是:更彻底地支持了面向对象、更强大的编译器。

  所以,本文就来说说,真正的Xmas。

 

  一、目标

  一门语言有什么目标吗?比如C#什么的;可惜的是我并不关心。作为一门脚本语言,其目的无非只有一个:简化编程。方式,也是直接明了:在更高的抽象上构建代码。那么什么是“更高的抽象”?个人认为,大抵是两点:1、更方便的操作语义 2、更多的限制。

  第一点,是指Xmas可以几乎毫无代价地大量使用宿主语言的现有代码;而且是以【语法】的方式。比如,Xmas要有正则表达式,可以有.....只需要使用XmasRef包装宿主语言中的现有Regex即可。

class Regex: Xmas{
    function native Regex(pattern);
    function native match(str);
    function native isMatched(str);
}

  事实上,Xmas所有的库函数,都只是C++中本人代码库中的对应物。——这正是脚本,所存在的意义(一旦开始自己构造自己,如C++等,便不再是脚本了)。

  第二点,这其实是由第一点造成的。很多东西一旦固化到语言本身,便意味着“不可改变”(永远地)。抽象程度越高,意味着,离二进制越远,能控制的东西,更少。最大的限制就是:有了GC,内存是什么?可以吃的吗?在代码里,永远地消失了(意味着,你不再可以控制)。

  最近,其实一直在思考一个不可能的话题:是否将静态类型加入到Xmas中?  之所以不可能,Xmas只是一门脚本语言。

  所以,下面,我们就来聊聊“脚本”。

 

  二、动态

  一门合格的动态语言:其必须可以【本能】地识别“字符串”。或者说,所有的东西都可以通过“字符串”来构造(替代):

    func = new Function("print");
    val  = new Integer("12");
    func(val);//打印一个整型12

  通过字符串,我们构造了一个函数(库打印函数print)和一个数字12。当然,我们可以更彻底一点:

    print  = new Function("print");
    intctr = new Function("Integer");
    val    = intctr("12");
    print(val);

  是的,在Xmas中,几乎一切都是【函数】。而函数本身,只是由字符串构成的标识符而已;所以,我们可以推迟一切的操作,变成另一门真正的只有函数的语言:

function doSome(str1, str2, str3)
{
    func1 = new Function(str1);
    func2 = new Function(str2);
    value = func2(str3);
    func1(value);
}

  这时,我们能够通过上面的代码,做任何能够想象的事情(只要有对应的函数,和对应的逻辑)。但是,这种“究极”的动态,有意义吗?没有。因为,上面的代码除了符合Xmas的语法外,并没有任何可识别的逻辑。

    doSome("print", "Integer", "12");
    print(12);

  上面两行代码,执行了相同的逻辑:打印数字12。

  动态能力,是为了在必要的时候,绕过编译器的语法/语义检查,而通过“自定义”的方式,访问Xmas的运行时。这个能够在很多时候是有那么一些用处的:

function to_string(val)
{
    if(typedef(val.toString) == Function && Function.argNum(val.toString) == 0){
        return val.toString();
    }
    return new String(val);
}

  这是早些时候,Xmas将一个对象转化为String的函数。在动态语言中,很多动态性是直接由【语法】本身直接提供的;这也正是“动态”之处。而其他类型的语言,想要达到相同的目的:1、通过反射(java/c#等)2、通过类型系统(C++的类型萃取,通过模板元编程)。

  当然,这一切是有足够的代价的:更慢、更多的错误。

 

  三、便捷

    var1 = new Exp.Var(new var1);
    var2 = new Exp.Var(new var2);
    cond = new Exp.Less(var1, var2);
    set1 = new Exp.Set(new var3, var1);
    set2 = new Exp.Set(new var3, var2);
    exp1 = new Exp.Select(cond, set1, set2);
    exp2 = new Exp.Set(new var4, new Exp.Val(12));
    exps = new Exp.Exps();
    exps.addExp(exp1);
    exps.addExp(exp2);
    env = new Exp.Env();
    env.addVar(new var1, 11);
    env.addVar(new var2, 2);
    exps.exec(env);
    put(exps);
    put(env);

  上面的代码,是以面向对象的方式构建了一颗表达式树(可对应C#);其执行后的结果:

    ((var1 < var2) ? var3 = var1 : var3 = var2)
    var4 = Integer:12
    //以上,对应了put(exps),打印表达式树本身

    var3:2 var4:12 var1:11 var2:2
    //以上,是树执行后的结果

  上面的代码只是干了两件事:1、获取var1与var2中的最小值,并将其赋给var3   2、将var4设置为数字12

  这些代码本身没有任何惊奇的地方;甚至有些多余:

    var1 = 11;
    var2 = 2;
    var3 = eval("function main(var1, var2){ if(var1 < var2) return var1; return var2;}", var1, var2);
    var4 = 12;

  这四行代码干了同样的事情,以更清晰和动态的方式。所以,重要的并非这些代码,而是支撑这些代码的“代码”,居然出奇的少:只有97行。

 1 class Exp{
 2 class Env: Xmas{
 3  function Env(){
 4         this.vars = new Map();
 5     }
 6  function addVar(label, val){
 7         this.vars[label] = val;
 8     }
 9  function setVar(label, val){
10         this.vars[label] = val;
11     }
12     function var(label){
13         return this.vars[label];
14     }
15  function toString(){
16         ret new String(this.vars);
17     }
18 }
19 class Val: Xmas{
20  function Val(val){
21         this.val = val;
22     }
23  function exec(){
24         return this.val;
25     }
26  function toString(){
27         return String.format("{}:{}", typeof(this.val), this.val);
28     }
29 }
30 class Var: Xmas{
31  function Var(label){
32         this.label = label;
33     }
34  function exec(env){
35         return env.var(this.label);
36     }
37  function toString(){
38         return new String(this.label);
39     }
40 }
41 class Set: Xmas{
42  function Set(label, val){
43         this.label = label;
44         this.val = val;
45     }
46  function exec(env){
47         env.setVar(this.label, this.val.exec(env));
48     }
49  function toString(){
50         ret String.format("{} = {}", this.label, this.val.toString());
51     }
52 }
53 class Less: Xmas{
54  function Less(a, b){
55         this.a = a;
56         this.b = b;
57     }
58  function exec(env){
59         return this.a.exec(env) < this.b.exec(env);
60     }
61  function toString(){
62         return String.format("({} < {})", this.a.toString(), this.b.toString());
63     }
64 }
65 class Select: Xmas{
66  function Select(condition, a, b){
67         this.condition = condition;
68         this.a = a;
69         this.b = b;
70     }
71  function exec(env){
72         if(this.condition.exec(env)){
73             return this.a.exec(env);
74         }
75         return this.b.exec(env);
76     }
77  function toString(){
78         return String.format("({} ? {} : {})", this.condition.toString(), this.a.toString(), this.b.toString());
79     }
80 }
81 class Exps: Xmas{
82  function Exps(){
83         this.exps = new Array();
84     }
85  function addExp(exp){
86         Array.push_back(this.exps, exp);
87     }
88  function exec(env){
89         foreach(this.exps, function(exp){ exp.exec(env);});
90     }
91  function toString(){
92         str = new Object{ str : ""};
93         foreach(this.exps, function(exp){ str.str += exp.toString() + String.line();});
94  ret str.str;
95     }
96 }
97 }//CLASS OF EXPRESSIONS

  我并不知道C#中为了相同的目的,需要多少行代码的支持;但无疑,会高一个数量级。这正是脚本的力量,Xmas的力量——可以更快,更高效地构建我们想要的东西。

 

  四、结语

  我有在想过,是否要讲讲Xmas的编译器?词法分析、语法分析、代码生成.......

  也有思考有没有必要说说Xmas的虚拟机?GC、运行时环境、字节码......

  无疑,我都不想讲。毕竟那些都是那么低枯燥而乏味。

  Xmas本身肯定不止前面所讲的这些;但,又如何?其不过是本人库中的一个较大的子模块,而已。所以,本系列,完。

  PS:另外的两个系列,多半是要“太监”。