众所周知,Roslyn project已经开源一年多了。简单的说,Roslyn是:1)用C#/VB写的C#/VB的编译器,以及与IDE集成;2)编译器的功能以API的方式暴露出来(即一组DLL)。
Roslyn对大多数开发者来说没啥用处,你关心的是你的应用程序的逻辑与构建而不需要关心编译器是怎么运行的。有时你觉得C#/VB有需要增强的地方,于是你兴致勃勃的跑到Roslyn论坛去发表一个proposal,MS的guy会给你的issue打上Area-Language Design
的标签,网友们也许会来讨论一下,然后呢?就没有然后了,这有点像在求雨,下不下、什么时候下、下多大全凭神仙们(MS guys)的“心情”。
求人不如求己,既然Roslyn已经以MIT方式开源,咱们就站在巨人的肩膀上自己动手丰衣足食吧,也就是,扩展C#,做(屌丝级的)complier guy!
众所周知,C#是门general-purpose的编程语言,你想添加的feature既可以是general的,也可以是specific的。比如,XML literal是个general的C# feature,下两图展示了一个specific的feature,让C#支持Windows Workflow Foundation(WF)的activity:
其实,这不该叫“扩展C#”,而该叫做“创建一门衍生自C#的DSL”,如果你对WF感兴趣,请访问
Metah.W: A Workflow Metaprogramming Language。
姑且就用“扩展C#”这个叫法。如上两图所示,Metah.W(MW)在C#中加入了activity的构造,在概念上,activity是C# class和function的合体,在图一中,public sealed class
摇身一变成为public sealed activity
,接着宣告activity的名字,小括号中宣告parameters,如string BookmarkName, string Text
,接着宣告可选的return type,如activity Prompt
的as int
宣告返回类型是int
,若无as XXX
宣告则返回void
。Activity的body中是变量的宣告和statement的使用,statement可以是:1)C# expression statement,如图一中的target = new Random().Next(1, MaxNumber) + 1;
;2)well-known statement,如if-else, while, do-while, foreach, try-catch-finally等;3)special statement,如图二中的statemachine
, delay
。因为activity是class和function的合体,可以调用它并将返回值赋给变量/参数,如guess = new Prompt().Invoke(...)
。
将FirstLook.mw送进MW编译器,将产生下面的C#代码:
//FirstLook.mw.cs, generated by the MW compiler
namespace HelloMW.FirstLook
{
public sealed class SequentialNumberGuess : global::System.Activities.Activity
{
public global::System.Activities.InArgument<int> MaxNumber { get; set; }
public global::System.Activities.OutArgument<int> Turns { get; set; }
private global::System.Activities.Activity __GetImplementation__()
{
global::System.Activities.Activity __vroot__;
{
var __v__0 = new global::System.Activities.Statements.Sequence();
var target = new global::System.Activities.Variable<int>();
__v__0.Variables.Add(target);
var guess = new global::System.Activities.Variable<int>();
__v__0.Variables.Add(guess);
__v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
{
target.SetEx(__ctx__, new Random().Next(1, MaxNumber.Get(__ctx__)) + 1);
}
));
var __v__1 = new global::System.Activities.Statements.DoWhile();
__v__1.Condition = new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) != target.Get(__ctx__));
{
var __v__2 = new global::System.Activities.Statements.Sequence();
__v__2.Activities.Add(new Prompt().Initialize(__activity2__ =>
{
__activity2__.BookmarkName = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => "EnterGuess"));
__activity2__.Text = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => "Please enter a number between 1 and " + MaxNumber.Get(__ctx__)));
__activity2__.Result = new global::System.Activities.OutArgument<int>(new global::MetahWLocationActivity<int>(guess));
}
));
__v__2.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
{
Turns.SetEx(__ctx__, __val__ => ++__val__, true);
}
));
var __v__3 = new global::System.Activities.Statements.If();
__v__3.Condition = new global::System.Activities.InArgument<bool>(new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) != target.Get(__ctx__)));
var __v__4 = new global::System.Activities.Statements.If();
__v__4.Condition = new global::System.Activities.InArgument<bool>(new global::MetahWFuncActivity<bool>(__ctx__ => guess.Get(__ctx__) < target.Get(__ctx__)));
__v__4.Then = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Your guess is too low.");
}
);
__v__4.Else = new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine("Your guess is too high.");
}
);
__v__3.Then = __v__4;
__v__2.Activities.Add(__v__3);
__v__1.Body = __v__2;
}
__v__0.Activities.Add(__v__1);
__vroot__ = __v__0;
}
return __vroot__;
}
private global::System.Func<global::System.Activities.Activity> __implementation__;
protected override global::System.Func<global::System.Activities.Activity> Implementation
{
get
{
return __implementation__ ?? (__implementation__ = __GetImplementation__);
}
set
{
throw new global::System.NotSupportedException();
}
}
}
public sealed class Prompt : global::System.Activities.Activity<int>
{
public global::System.Activities.InArgument<string> BookmarkName { get; set; }
public global::System.Activities.InArgument<string> Text { get; set; }
private global::System.Activities.Activity __GetImplementation__()
{
global::System.Activities.Activity __vroot__;
var __v__0 = new global::System.Activities.Statements.Sequence();
__v__0.Activities.Add(new global::MetahWActionActivity(__ctx__ =>
{
Console.WriteLine(Text.Get(__ctx__));
}
));
__v__0.Activities.Add(new ReadInt().Initialize(__activity2__ =>
{
__activity2__.BookmarkName = new global::System.Activities.InArgument<string>(new global::MetahWFuncActivity<string>(__ctx__ => BookmarkName.Get(__ctx__)));
__activity2__.Result = new global::System.Activities.OutArgument<int>(new global::MetahWLocationActivity<int>(Result));
}
));
__vroot__ = __v__0;
return __vroot__;
}
private global::System.Func<global::System.Activities.Activity> __implementation__;
protected override global::System.Func<global::System.Activities.Activity> Implementation
{
get
{
return __implementation__ ?? (__implementation__ = __GetImplementation__);
}
set
{
throw new global::System.NotSupportedException();
}
}
}
}
这就是元编程,把语法糖解糖的过程,即把高级抽象的描述翻译成低级具体的实现。我觉得,“语法糖”是个肤浅的认识,实际上,多数的“语法糖”都涉及到语义,不仅仅是简单的语法转换。元编程的另一个例子,早期某些C++编译器能将C++代码翻译成等价的C代码,即C++是门元编程语言,它是C的“语法糖”。
日光之下,并无新事。元编程是个非常“古老”的概念,但在每个“时代”它都能玩出耳目一新的花样。
欲知后事如何,请听下回分解。