像C#或者Haskell这样的先进的语言都有一个跟语法分不开的最核心的库。譬如说C#的int,是mscorlib.dll里面的System.SInt32,Haskell的(x:xs)则定义在了prelude里面。Vczh Library++ 3.0的ManagedX语言也有一个类似mscorlib.dll的东西。之前的NativeX提供了一个核心的函数库叫System.CoreNative (syscrnat.assembly),因此ManagedX的就命名为System.CoreManaged (syscrman.assembly)。System.CoreManaged里面的预定义对象都是一些基本的、不可缺少的类型,例如System.SInt32、System.IEnumerable<T>或者System.Reflection.Type。昨天晚上我的未完成的语义分析器的完成程度已经足以完全分析System.CoreManaged里面的托管代码了,因此符号表里面的类型系统也基本上是一个完整的类型系统。在开发的过程中得到的心得体会便是写着一篇文章的来源。
如今,先进的面向对象语言的类型都离不开下面的几个特征:对象类型、函数类型和接口类型。修饰类型的工具则有泛型和延迟绑定等等。譬如说C#,对象类型便是object,函数类型则有.net framework支持的很好,但是不是核心类型的Func和Action,接口类型则类似IEnumerable。泛型大家都很熟悉,延迟绑定则类似于dynamic关键字。var关键字是编译期绑定的,因此不计算在内。Java的int是魔法类型,其设计的错误已经严重影响到了类库的优美程度,其使用“类型擦除”的泛型系统也为今后的发展留下了一些祸根,因此这些旁门左道本文章就不去详细讨论了。这篇文章讲针对重要的那三个类型和两个修饰进行讨论,并解释他们之间互相换算的方法。
在C#里面,函数类型也是对象类型的一部分,但是由于C#可以在编译过程中把一个不完整的函数类型推导为一个完整的函数类型,因此在这里将它和对象类型区分开来。Haskell则在推导上做得更加彻底,这都是先进的有类型语言所不可缺少的一个特征。由于类型之间的互相换算是本文所关心的内容,因此下面先给出几个定义。当然这些定义在数学上是不严谨的,而我也并不追求这个。namespace在这里也不是非常重要,因为存在namespace和不存在namespace所带来的区别仅仅是一个对象被如何解释(黑话称之为Resolving),并不影响推导过程。
我们可以将一个类型命名为T,它是不带泛型的。一般来说,因为类型存在成员函数,所以类型便有几个基本的属性,称之为this类型和base类型(在C#,代表自己的关键字分别是this和base)。this指的是类型T的成员函数所看到的自己的类型。而base类型则是父类的类型。在这里有必要做出一点解释。只有对象类型才具有base类型,而且其base类型指的是所有父类中唯一一个不是接口类型的那个。函数类型和接口类型都有this类型。
因此对于任何一个具有下面描述的类型T:
class T : U, I1, I2, I3{}
this(T) == T
base(T) == U
现在让我们来考察一个带泛型的类型声明T[U, V],和他的实例化类型T<A, B>之间的关系。我们知道,一个带泛型的类型声明T[U, V]实际上是一个不完整的类型,因为这个类型还有U和V两个参数待填,正如下面的代码所示:
class T<U, V>{}
而当你实例化他之后,令U==A,V==B,则T类型被A和B实例化成了T<A, B>。这就有点象我们把一个Dictionary[K, V]给实例化成Dictionary<int, string>一样。一个实例化后的类型才可以被当成另一个泛型类型的类型参数,或者直接使用它来定义一些符号,或者创建一个它的实例等等。但是不完整的泛型类型T[U, V]和它的实例化类型T<A, B>都具有共同的属性——this类型和base类型。按照上面的定义,this类型是该类型的成员函数所看到的自己的类型。
因此对于任何一个具有下面描述的类型T[U, V]:
class T<U, V> : W<U, V>{}
this(T[U, V]) == T<U, V>
base(T[U, V]) == W<U, V>
当然,对于T<A, B>来说,它也具有this类型T<A, B>和base类型W<A, B>。一般情况下,非泛型类型T的声明可以被处理成T[],我们令T[]等于T<>,就可以将所有泛型类型的规则实例化到一个带有0个泛型参数的泛型类型——也就是非泛型类型上面了。因此下面的讨论将不作区分。
现在我们考虑如何获得一个泛型类型的所有成员的类型。我们考虑下面的一组类型:
interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
class Base<T> : IEnumerable<T>
{
public T Value{get; set;}
}
class Derived<T, U> : Base<U>
{
}
我们来考虑一个问题:如何知道Derived<int, string>的GetEnumerator函数的返回值类型是什么呢?乍一看似乎很简单,其实对于人类来说这个问题的确是仅靠直觉就可以瞬间回答出来的、根本没有任何障碍的问题了。这里我一直佩服大自然可以将人类进化到如此牛逼的地步。不过这个问题困扰了我很久,主要是在开发语义分析器的时候,安排各种各样的类型运算、符号表的结构和其它的一些相关问题的时候,这个问题的难度就提高了。
不过在这里我并不想多说什么废话,我们仅需要给类型对增加几个属性和运算规则,就可以很容易的将这个问题组合成一个表达式了。
首先,我们需要有一个replace操作。replace操作很难一下子严谨的定义出来,不过可以给一个直观的定义,就是:
replace(Derived<T, U>, {T=>int, U=>string}) == Derived<int, string>
相信大家已经可以很轻松的理解了,因此对于一个类型映射tm={T=>string}来说,replace(Derived<IEnumerable<T>>, tk)的结果就是Derived<IEnumerable<string>>了。
其次,我们需要一个decl操作,这个操作返回一个泛型类型的实例类型的定义:
decl(T<A, B>) == T[U, V]
然后,我们还需要一个params操作。这个操作将一个泛型类型的实例类型和他的泛型定义相比较,提取出可以从泛型定义replace到实例类型的那个类型映射:
params(T<A, B>) == {T=>A, U=>B}
因此一般来说,我们有下面的规则。只要类型T是一个泛型类型的实例类型,那么总是有:
replace(this(decl(T)), params(T)) == T
现在我们就可以开始回答上面提到的那个问题了。
首先对于类型Derived<int, string>,我们需要找到他的父类。因此我们可以做如下几步操作:
tm = params(Derived<int, string>) = {T=>int, U=>string}
tb = base(decl(Derived<int, string>)) = base(Derived[T, U]) = Base<U>
result = replace(tb, tm) = replace(Base<U>, {T=>int, U=>string}) = Base<string>
这样我们就成功求出T=Derived<int, string>的父类B=replace(base(decl(T)), params(T))=Base<string>了
其次,我们指定要计算类型Base<string>所继承的那个接口Base[T]=>IEnumerable<T>,我们可以使用
tm = params(Base<string>) = {T=>string}
result = replace(IEnumerable<T>, tm) = IEnumerable<string>
因此对于一个泛型声明decl(T)所继承的一个接口Id,泛型声明D的实例类型T所对应的接口It等于replace(Td, params(T))。
因此对于IEnumerable[T]的函数GetEnumerator的返回值类型IEnumerator<T>,聪明的读者肯定想到,IEnumerable<string>所对应的类型就是replace(IEnumerator<T>, params(IEnumerable<string>)) == IEnumerator<string>了。这个结果跟求实例类型所继承的接口类型的方法一样。
我们可以知道,在计算泛型类型的实例类型的成员类型中,我们总是不断地在计算replace(A, params(B))的结果。因此在我实现的带泛型的面向对象托管语言:Vczh Library++ 3.0的ManagedX语言的语义分析器的符号表的代码里面,真实出现了使用C++所完成的this、base、decl、params、replace和replace_by_type = replace(A, params(B))这样的六个函数。因为在C++里面,一个类型的实例只能被表示为一个带有复杂结构的对象的指针。因此只要符号表在计算类型的过程中,把所有产生出来的类型保存下来,建立索引,并且使得“只要类型A和类型B是同一个类型则有他们的指针P(A)和P(B)相等”的这个条件恒成立的话,类型系统的计算速度将直接提高。
至于函数类型的推导法则(主要是应用于lambda表达式的缩写语法),则等到我开发到那里的时候再写后续的文章了。System.CoreManaged有幸不需要使用lambda表达式,使得我的第一个里程碑提前到来。
posted @
2011-09-27 05:54 陈梓瀚(vczh) 阅读(7080) |
评论 (4) |
编辑 收藏
C#真TMD复杂,让我好生搞了几个月都没把一个山寨C#的语法分析器写完,就想干脆去搞别的东西好了。因此山寨了个小WCF还顺带实践了一下不需要传递password和MD5(password)然后用RSA传递AESkey的加密方法,做了一个client可以new server的类来做网络通讯的东西。完了还搞了搞图像识别,不过最后除了一个超烂的边缘检测以外也没做成什么东西。
结果昨天晚上梦见我把C#编译到GPU上面的事情给搞定了(!!!!),然后在梦里面就在想:“现在距离javascript编译到GPU也不远了,干脆秒掉他好了。”正要开秒,就醒了。
所以想了想,从今天开始,还是继续做下去罢。
posted @
2011-09-11 10:13 陈梓瀚(vczh) 阅读(4826) |
评论 (18) |
编辑 收藏
今天终于把雏形给做出来了。主要的方法是牛顿迭代法,把屏幕上的所有点都收敛到函数图像上面。为了提速,我是用了ThreadTool.QueueUserWorkItem和Parallel.For,还把那颗函数的语法树用Linq.Expression编译成了机器码。下面的这些图都是二十秒钟左右就可以画出来的了。代码仍然在
Vczh Library++3.0的Candidate\Games\FunctionVisualizer里面。直接F5太慢,要编译后在资源管理器打开。
下面几个图来自于博客园的这篇新闻(
http://news.cnblogs.com/n/106212/)。因为我还没做绝对值函数,所以只画了一半。结果还是有点瑕疵,再想想办法优化一下。
posted @
2011-08-10 22:36 陈梓瀚(vczh) 阅读(5849) |
评论 (10) |
编辑 收藏
摘要: 今天看到了校内上一个batman equation,觉得很顺不舒服。第一个是因为我觉得那个图是错的,第二个是因为这让我开始思考如何对任意的f(x, y)进行绘制。其实这是个很困难的问题。但是如果我假设f(x, y)是处处可微的,那么问题说不定会简单一点。因此今天晚上就忍不住开始写了。我的想法是,对于屏幕上的所有点,分别令x或者y等于该点的其中一个坐标元素,对f...
阅读全文
posted @
2011-08-10 10:40 陈梓瀚(vczh) 阅读(4702) |
评论 (9) |
编辑 收藏
自己本来想体验一下开发一个简陋的模型编辑器以便于把我高中做的那个破即时RPG游戏给弄个续集的,没想到这东西也不是一件好差事啊。先上图。
这个模型编辑器才刚刚开始做,菜单都还没做完整。暂时有添加模型、选择模型、平面或者点、旋转放大平移选中的模型或他的一部分。这样就花了我两三个星期了。DirectX11竟然没有D3D11Font,结果10的Font、D2D10和D3D11Device结合起来又十分麻烦,因此我才去了个猥琐的做法:创建DIBSections,用GDI画字,然后逐个像素变换成D3D11Texture2D需要的格式,最后贴上去。点的高亮矩形也是这么干的。
然后我创建了一个sphere,然后拖动几个选中的点,因此就变成了这样……
(代码可以在
Vczh Library++3.0的Candidate\Simulator\DirectX\EditorSolution找到)
posted @
2011-08-03 04:54 陈梓瀚(vczh) 阅读(4404) |
评论 (6) |
编辑 收藏
摘要: Vczh Library++3.0的山寨C#的ManagedX今天完成了一个功能,就是编译器检查一个类型在他上下文里面是否可用。这个过程足够复杂,我写了足足3个小时。 ManagedX的符号表里面的类型已经被大大简化了。函数指针是类,基本数字类型也是类,所以归根结底只有 1:子类型&nbs...
阅读全文
posted @
2011-07-16 01:38 陈梓瀚(vczh) 阅读(2836) |
评论 (1) |
编辑 收藏
摘要: DirectX最振奋人心的一点就是,取消了所有固定管线,功能全部用shader来做。这样就再也不需要受到各种功能上的限制的束缚了。譬如opengl旧版本的时候,可以输入多个贴图,然后每一层的贴图给一个blend function,搞得好生复杂。到了后来,大家都不用固定管线了。现在DirectX11直接取消了固定管线,API反而简洁了许多,也不会有各种鸟事发生。...
阅读全文
posted @
2011-07-15 04:25 陈梓瀚(vczh) 阅读(10170) |
评论 (17) |
编辑 收藏
摘要: 其实编译器也不是不做了,只是长时间连续做一个不能看的编译器,总是有点不爽。因此我决定把业余的时间的主要部分花在编译器上,而次要的部分则玩游戏,出去游乐,学DirectX11。 在我还是14岁的时候那会,刚开始学习编程不久就买了那本《Visual Basic高级图形程序设计教程》,从此我走上了做图形的道路上。后来做游戏,到了大...
阅读全文
posted @
2011-07-08 08:18 陈梓瀚(vczh) 阅读(4992) |
评论 (11) |
编辑 收藏
摘要: 由于Vczh Library++3.0的托管语言ManagedX被设计成编译到本地语言NativeX,因此山寨一个mscorlib.dll是必不可少的。不过我的mscorlib.dll只包含最低限度的代码。譬如说string,譬如说数组,譬如说函数类型等等这些本不能用托管语言自己来实现的类(C++是唯一的一个所有东西都可以用类库来弥补的语言)。因此花费了数日,...
阅读全文
posted @
2011-06-26 09:15 陈梓瀚(vczh) 阅读(3987) |
评论 (17) |
编辑 收藏
Vczh Library++3.0的ManagedX(山寨C#)语法分析器写好了。将近1000行的语法树声明,使用了ParserCombinator还有93k的语法分析器。写了好久。其中遇到了一些问题,譬如说C#的语法实在太复杂,parse一个method也好property也好都会有一大堆东西。举个例子,一个method的文法如下:
1 (attributeInfo + opt(genericInfo) + accessor + memberType + inheritation + internalExternal +
2 type +
3 opt(type << COLON(NeedColon) << COLON(NeedColon)) + ID(NeedId) +
4 (OPEN_EXP_BRACE(NeedOpenExpBrace) >> plist(opt(parameter + *(COMMA >> parameter))) << CLOSE_EXP_BRACE(NeedCloseExpBrace)) +
5 statement
6 )[ToMethodMember]
7
最顶级的operator+一共有10个,也就是说这个东西的返回结果是pair<pair<pair<pair<pair<pair<pair<pair<pair<pair<a, b>, c>, d>, e>, f>, g>, f>, i>, j>, k>。因此ToMethodMember函数的参数也是这个类型。这显然很令人讨厌。
再举一个例子,property的文法如下:
1 (attributeInfo + accessor + memberType + inheritation + type + opt(type << COLON(NeedColon) << COLON(NeedColon)) + ID(NeedId) +
2 (OPEN_DECL_BRACE(NeedOpenDeclBrace) >> (
3 opt(GET >> statement) +
4 opt(opt(setterAccessor) + (SET >> statement))
5 ) << CLOSE_DECL_BRACE(NeedCloseDeclBrace))
6 )[ToPropertyMember]
7
这个东西的返回结果是pair<pair<pair<pair<pair<a, b>, c>, d>, e>, pair<list<f>, list<list<pair<g, h>>>>。写起来也很令人发疯。因此这几天就想了一种方法来解决这种问题。
首先,我们一定要采取一种方法来让这种火箭一样的代码给平坦化。由于operator+的左结合特性,实际上我们无法去掉这些pair,因此只能换一种方法,譬如说让pair<pair<pair<a, b>, c>, d>总是等价于tuple<a, b, c, d>。这显然是可能的,只需要重载足够数量的tuple类型,就可以让typename tuple<a, b, c, d>::ResultType等于pair<pair<pair<a, b>, c>, d>。
其次,当我们面对这些pair<pair<pair<a, b>, c>, d>的时候,如何将他赋值到一个struct呢?假设struct的声明如下:
1 struct s
2 {
3 a _a;
4 b _b;
5 c _c;
6 d _d;
7 };
我们可以用下面的代码:
1 struct s;
2
3 auto x = ref(s._a)
4 .ref(s._b)
5 .ref(s._c)
6 .ref(s._d)
7 ;
来让x等于pair<pair<pair<*a, *b>, *c>, *d>。因为“点”也是左结合的。后面只需要再用模板元编程就可以把pair<pair<pair<a, b>, c>, d>赋值给pair<pair<pair<*a, *b>, *c>, *d>了。
让我们看看
Vczh Library++3.0源代码(Library\Scripting\Languages\ManagedX\ManagedXParser_Declaration.cpp)在使用了这个构造之前和之后的代码。首先是直接使用和读取pair的:
1 Ptr<ManagedMember> ToPropertyMember(const ParsingPair<ParsingPair<ParsingPair<ParsingPair<ParsingPair<ParsingPair<ParsingPair<
2 Ptr<ManagedAttributeInfo>,
3 declatt::Accessor>,
4 declatt::MemberType>,
5 declatt::Inheritation>,
6 Ptr<ManagedType>>,
7 ParsingList<Ptr<ManagedType>>>,
8 RegexToken>,
9 ParsingPair<
10 ParsingList<Ptr<ManagedStatement>>,
11 ParsingList<ParsingPair<ParsingList<declatt::Accessor>, Ptr<ManagedStatement>>>
12 >>& input)
13 {
14 Ptr<ManagedProperty> member=CreateNode<ManagedProperty>(input.First().Second());
15 CopyAttributeInfo(member->attributeInfo, input.First().First().First().First().First().First().First());
16 member->accessor=input.First().First().First().First().First().First().Second();
17 member->memberType=input.First().First().First().First().First().Second();
18 member->inheritation=input.First().First().First().First().Second();
19 member->type=input.First().First().First().Second();
20 if(input.First().First().Second().Head())
21 {
22 member->implementedInterfaceType=input.First().First().Second().Head()->Value();
23 }
24 member->name=ConvertID(WString(input.First().Second().reading, input.First().Second().length));
25
26 member->setterAccessor=member->accessor;
27 if(input.Second().First().Head())
28 {
29 member->getter=input.Second().First().Head()->Value();
30 }
31 if(input.Second().Second().Head())
32 {
33 if(input.Second().Second().Head()->Value().First().Head())
34 {
35 member->setterAccessor=input.Second().Second().Head()->Value().First().Head()->Value();
36 }
37 member->setter=input.Second().Second().Head()->Value().Second();
38 }
39 return member;
40 }
41
其次是用tuple和ref来赋值的:
1 Ptr<ManagedMember> ToPropertyMember(const x::tp<
2 Ptr<ManagedAttributeInfo>,
3 declatt::Accessor,
4 declatt::MemberType,
5 declatt::Inheritation,
6 Ptr<ManagedType>,
7 x::opt<Ptr<ManagedType>>,
8 RegexToken,
9 x::tp<
10 x::opt<Ptr<ManagedStatement>>,
11 x::opt<x::tp<x::opt<declatt::Accessor>, Ptr<ManagedStatement>>>
12 >
13 >::ResultType& input)
14 {
15 Ptr<ManagedProperty> member=CreateNode<ManagedProperty>(input.First().Second());
16 x::Fill(
17 x::ref(member->attributeInfo)
18 .ref(member->accessor)
19 .ref(member->memberType)
20 .ref(member->inheritation)
21 .ref(member->type)
22 .ref(member->implementedInterfaceType)
23 .ref(member->name)
24 .ref(
25 x::ref(member->getter)
26 .ref(
27 x::ref(member->setterAccessor)
28 .ref(member->setter)
29 )
30 )
31 , input);
32 member->name=ConvertID(member->name);
33 return member;
34 }
35
其简洁程度完全不同。
posted @
2011-06-13 23:01 陈梓瀚(vczh) 阅读(2944) |
评论 (0) |
编辑 收藏