自己动手开发编译器(十一)语义分析

上回我们已经用VBF的Parsers.Combinators库生成了miniSharp的语法分析器,并且能够将miniSharp的源代码翻译成抽象语法树(AST)。这一回我们要继续进行下一步------语义分析。就目前大家接触的编程语言,如C#、VB、C++来说,语义分析是编译器前端最复杂的部分。因为这些编程语言的语义都非常复杂。语义分析不像之前词法分析、语法分析那样,有一些特定的工具来帮助。这一部分通常都是要纯手工写代码来完成。我们的miniSharp语义因为已经高度简化,它的语义分析可以说比C#要容易一个数量级。我们只会在选定方法重载的时候见识一下C#复杂语义的冰山一角。

所谓编程语言语义,就是这段代码实际的含义。编程语言的代码必须有绝对明确的含义,这样人们才能让程序做自己想做的事情。比如最简单的一行代码:a = 1; 它的语义是"将32位整型常量存储到变量a中"。首先我们对"1"有明确的定义,它是32位有符号整型字面量,这里"32位有符号整型"就是表达式"1"的类型 。其次,这句话成为合法的编程语言,32位整型常量必须能够隐式转换为a的类型。假设a就是int型变量,那么这条语句就直接将1存储到a所在内存里。如果a是浮点数类型的,那么这句话就隐含着将整型常量1转换为浮点类型的步骤。在语义分析中,类型检查是贯穿始终的一个步骤。像miniSharp这样的静态类型语言,类型检查通常要做到:

  1. 判定每一个表达式的声明类型
  2. 判定每一个字段、形式参数、变量声明的类型
  3. 判断每一次赋值、传参数时,是否存在合法的隐式类型转换
  4. 判断一元和二元运算符左右两侧的类型是否合法(比如+不就不能在bool和int之间进行)
  5. 将所有要发生的隐式类型转换明确化

要进行以上操作,需要一个表存储所有已知的类型。如果引用了外部程序集,则也需要将外部程序集中的类型信息放到表中。类型信息包括类型的名字、父类(如果有的话)、成员以及相互隐式转换的规则。我们用如下的类来表示一个miniSharp自定义类型:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">CodeClassType </span>: <span style="color:#2b91af">TypeBase </span>{ <span style="color:#0000ff">public bool </span>IsStatic { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; } <span style="color:#0000ff">public </span><span style="color:#2b91af">CodeClassType </span>BaseType { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; } <span style="color:#0000ff">public </span><span style="color:#2b91af">Collection</span><<span style="color:#2b91af">Method</span>> Methods { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">private set</span>; } <span style="color:#0000ff">public </span><span style="color:#2b91af">Collection</span><<span style="color:#2b91af">Method</span>> StaticMethods { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">private set</span>; } <span style="color:#0000ff">public </span><span style="color:#2b91af">VariableCollection</span><<span style="color:#2b91af">Field</span>> Fields { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">private set</span>; } <span style="color:#0000ff">public </span>CodeClassType() { Methods = <span style="color:#0000ff">new </span><span style="color:#2b91af">Collection</span><<span style="color:#2b91af">Method</span>>(); StaticMethods = <span style="color:#0000ff">new </span><span style="color:#2b91af">Collection</span><<span style="color:#2b91af">Method</span>>(); Fields = <span style="color:#0000ff">new </span><span style="color:#2b91af">VariableCollection</span><<span style="color:#2b91af">Field</span>>(); } <span style="color:#0000ff">public override bool </span>IsAssignableFrom(<span style="color:#2b91af">TypeBase </span>type) { <span style="color:#2b91af">CodeClassType </span>otherClassType = type <span style="color:#0000ff">as </span><span style="color:#2b91af">CodeClassType</span>; <span style="color:#0000ff">if </span>(otherClassType == <span style="color:#0000ff">null</span>) { <span style="color:#0000ff">return false</span>; } <span style="color:#0000ff">if </span>(otherClassType == <span style="color:#0000ff">this</span>) { <span style="color:#0000ff">return true</span>; } <span style="color:#0000ff">else </span>{ <span style="color:#0000ff">return </span>IsAssignableFrom(otherClassType.BaseType); } } } </span> |

miniSharp不支持显式类型转换,而唯一支持的隐式类型转换是子类引用到父类引用的转换。

除了自定义类型之外,我们还需要表示数组类型和基元类型(int和bool),简陋地如下处理:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">PrimaryType </span>: <span style="color:#2b91af">TypeBase </span>{ <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">PrimaryType </span>Int = <span style="color:#0000ff">new </span><span style="color:#2b91af">PrimaryType</span>() { Name = <span style="color:#a31515">"int" </span>}; <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">PrimaryType </span>Boolean = <span style="color:#0000ff">new </span><span style="color:#2b91af">PrimaryType</span>() { Name = <span style="color:#a31515">"bool" </span>}; <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">PrimaryType </span>String = <span style="color:#0000ff">new </span><span style="color:#2b91af">PrimaryType</span>() { Name = <span style="color:#a31515">"string" </span>}; <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">PrimaryType </span>Void = <span style="color:#0000ff">new </span><span style="color:#2b91af">PrimaryType</span>() { Name = <span style="color:#a31515">"void" </span>}; <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">PrimaryType </span>Unknown = <span style="color:#0000ff">new </span><span style="color:#2b91af">PrimaryType</span>() { Name = <span style="color:#0000ff">null </span>}; <span style="color:#0000ff">public override bool </span>IsAssignableFrom(<span style="color:#2b91af">TypeBase </span>type) { <span style="color:#0000ff">if </span>(<span style="color:#0000ff">this </span>== type) { <span style="color:#0000ff">return true</span>; } <span style="color:#0000ff">else </span>{ <span style="color:#0000ff">return false</span>; } } } </span> code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">ArrayType </span>: <span style="color:#2b91af">TypeBase </span>{ <span style="color:#0000ff">public </span><span style="color:#2b91af">TypeBase </span>ElementType { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; } <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">ArrayType </span>IntArray = <span style="color:#0000ff">new </span><span style="color:#2b91af">ArrayType</span>() { Name = <span style="color:#a31515">"int[]"</span>, ElementType = <span style="color:#2b91af">PrimaryType</span>.Int }; <span style="color:#0000ff">public static readonly </span><span style="color:#2b91af">ArrayType </span>StrArray = <span style="color:#0000ff">new </span><span style="color:#2b91af">ArrayType</span>() { Name = <span style="color:#a31515">"string[]"</span>, ElementType = <span style="color:#2b91af">PrimaryType</span>.String }; <span style="color:#0000ff">public override bool </span>IsAssignableFrom(<span style="color:#2b91af">TypeBase </span>type) { <span style="color:#2b91af">CodeClassType </span>elementClassType = ElementType <span style="color:#0000ff">as </span><span style="color:#2b91af">CodeClassType</span>; <span style="color:#2b91af">ArrayType </span>arrayType = type <span style="color:#0000ff">as </span><span style="color:#2b91af">ArrayType</span>; <span style="color:#0000ff">if </span>(elementClassType != <span style="color:#0000ff">null </span>&& arrayType != <span style="color:#0000ff">null</span>) { <span style="color:#0000ff">return </span>elementClassType.IsAssignableFrom(arrayType.ElementType); } <span style="color:#0000ff">return false</span>; } } </span> |

实际上C#会将int和bool直接映射到System.Int32以及System.Boolean结构体。我们的miniSharp不仅仅要翻译成托管代码,所以并没有采用这个规定,但在生成IL的时候仍然做这样的特殊处理。最后因为miniSharp并不支持引用外部程序集,所以我也没有将类型表独立出来,而是将类型信息存储在每个表示class的语法树节点上,以方便语义分析时访问。

语义分析的第二个主要任务是找到所有标识符的定义。标识符在miniSharp里主要有:类名、字段名、方法名、参数名和本地变量名。遇到每个名称,我们必须解析出标识符表示的类、方法或字段的定义。比如下面这段代码:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">class </span><span style="color:#2b91af">MyClass </span>{ <span style="color:#0000ff">int </span>a; <span style="color:#0000ff">public int </span>Foo() { <span style="color:#0000ff">int </span>a; a = 1; <span style="color:#0000ff">if </span>(<span style="color:#0000ff">this</span>.a > 0) { <span style="color:#0000ff">return this</span>.a; } <span style="color:#0000ff">else </span>{ <span style="color:#0000ff">return </span>a; } } } </span> |

有一个字段叫a,在过程Foo中又定义了一个同名局部变量a。那么过程内的局部变量a就会覆盖字段的a,这句话的意思是标识符"a"在Foo中将表示局部变量,而不是同名字段。在语义分析里,我们遇到每一个可能代表变量的标识符时,都要按照一套预先设定的规则来寻找其定义。比如按照如下顺序:

  1. 搜索当前的本地符号表,其中包括当前作用域中定义的本地变量和方法参数
  2. 搜索当前类的字段

如果类的字段不仅仅是private的话,如果类还允许定义属性的话,这里的规则还要多好几条。所幸miniSharp只用以上两条就够了。我们看看怎么表示本地符号表。

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">VariableCollection</span><T> : <span style="color:#2b91af">KeyedCollection</span><<span style="color:#0000ff">string</span>, T> <span style="color:#0000ff">where </span>T : <span style="color:#2b91af">VariableInfo </span>{ <span style="color:#0000ff">private </span><span style="color:#2b91af">Stack</span><<span style="color:#2b91af">HashSet</span><<span style="color:#0000ff">string</span>>> m_levelStack; <span style="color:#0000ff">public int </span>m_Levels; <span style="color:#0000ff">public </span>VariableCollection() { m_Levels = 0; m_levelStack = <span style="color:#0000ff">new </span><span style="color:#2b91af">Stack</span><<span style="color:#2b91af">HashSet</span><<span style="color:#0000ff">string</span>>>(); } <span style="color:#0000ff">protected override string </span>GetKeyForItem(T item) { <span style="color:#0000ff">return </span>item.Name; } <span style="color:#0000ff">public void </span>PushLevel() { m_levelStack.Push(<span style="color:#0000ff">new </span><span style="color:#2b91af">HashSet</span><<span style="color:#0000ff">string</span>>()); m_Levels++; } <span style="color:#0000ff">public void </span>PopLevel() { <span style="color:#0000ff">if </span>(m_Levels == 0) { <span style="color:#0000ff">throw new </span><span style="color:#2b91af">InvalidOperationException</span>(); } <span style="color:#0000ff">var </span>keysInLevel = m_levelStack.Pop(); m_Levels--; <span style="color:#0000ff">foreach </span>(<span style="color:#0000ff">var </span>key <span style="color:#0000ff">in </span>keysInLevel) { Remove(key); } } <span style="color:#0000ff">protected override void </span>InsertItem(<span style="color:#0000ff">int </span>index, T item) { <span style="color:#0000ff">base</span>.InsertItem(index, item); <span style="color:#0000ff">if </span>(m_Levels > 0) { <span style="color:#0000ff">var </span>keysInLevel = m_levelStack.Peek(); keysInLevel.Add(GetKeyForItem(item)); } } } </span> |

为了简便处理这里所用的数据结构都比较粗糙。但基本思想是使用一个Stack,在进入一个新的作用域(大括号包围的语句块)时压入一个新的HashSet,储存这一作用域内声明的变量。当作用域结束时弹出一个HashSet,这个作用域内的变量就从表里删除了。所以,miniSharp允许两个不互相嵌套的语句块内定义同名变量,但不允许在同一个方法内的语句块内覆盖语句块外定义的变量或形式参数。

接下来我们要讨论方法重载选取的问题。这是miniSharp中唯一一个稍微有些复杂性的语义。miniSharp允许同一个类多个方法具有相同的方法名,只要他们的形式参数表的类型不完全一样即可。而判断一个方法调用表达式到底调用的是哪个方法,一共分为以下几个步骤。

  1. 第一步,找到当前类 中所有签名相符的方法。miniSharp和C#一样,当前类中的方法具有比父类更高的优先级。而VB则采取当前类和父类相同优先级(使用Overloads关键字时)。所以miniSharp也要先在当前类中搜索合适的候选。第二个条件是签名相符,它的定义是方法调用的表达式与候选方法的名称相同,参数列表长度一致,并且方法调用的表达式列表中的每一个表达式的类型,都能隐式转换成候选方法参数表中对应位置参数的类型。稍微形式化一下,就是方法F(T1, T2, T3,...,Tn)是调用表达式C(E1, E2, E3,... Em)的签名相符候选方法的条件是F.Name = C,m = n并且对所有i从1到n满足Ti.IsAssignableFrom(typeof(Ei))。
  2. 第二步,所有签名相符的候选方法中,找到一个最佳 候选。如果有两个候选方法P(P1, P2,...,Pn)和Q(Q1, Q2,...,Qn),那么我们说P比Q更佳 当且仅当:P的每一个参数类型都比Q的相应参数类型更好或至少一样好,同时Q的每一个参数类型都比P的相应参数类型更好。如果P和Q各自有一些参数类型比对方更好,那么就视为P和Q条件一致,无法做出判断(有歧义)。
  3. 调用表达式列表项E所对应的候选方法参数类型TP比TQ更好意味着:TP与typeof(E)相等但TQ与typeof(E)不相等;或者TQ.IsAssignableFrom(TP),这意味着TP比TQ更"具体"一些。如果TP和TQ之间无法相互隐式转换,或者两者是相同的类型,则视为无法区分。
  4. 如果在当前类中没有符合条件的候选,则对父类重复以上步骤。

真正C#的方法重载判断大体上也是这个步骤,但还要更加复杂得多。因为C#还有param数组型参数,可选参数,命名参数,泛型方法等语法。这里C#的Spec整整写了好几页纸来描述完整的规则。初看起来这段规则转换成代码很难写,所以我采用了一种取巧的方法:定义一个比较两个候选参数好坏的Comparer类,然后用Order By的方式对候选参数进行排序。Comparer类如下:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">MethodOverloadingComparer </span>: <span style="color:#2b91af">IComparer</span><<span style="color:#2b91af">Method</span>> { <span style="color:#0000ff">private </span><span style="color:#2b91af">IList</span><<span style="color:#2b91af">Expression</span>> m_expressionList; <span style="color:#0000ff">public </span>MethodOverloadingComparer(<span style="color:#2b91af">IList</span><<span style="color:#2b91af">Expression</span>> expressions) { <span style="color:#2b91af">Debug</span>.Assert(expressions != <span style="color:#0000ff">null</span>); m_expressionList = expressions; } <span style="color:#0000ff">public int </span>Compare(<span style="color:#2b91af">Method </span>x, <span style="color:#2b91af">Method </span>y) { <span style="color:#008000">//step 1. find one with better conversion. </span><span style="color:#0000ff">int </span>lastComparisonResult = 0; <span style="color:#0000ff">for </span>(<span style="color:#0000ff">int </span>i = 0; i < m_expressionList.Count; i++) { <span style="color:#0000ff">int </span>result = CompareConversion(x.Parameters[i].Type, y.Parameters[i].Type, m_expressionList[i]); <span style="color:#0000ff">if </span>(lastComparisonResult < 0 && result > 0 || lastComparisonResult > 0 && result < 0) { <span style="color:#008000">//none is better </span><span style="color:#0000ff">return </span>0; } <span style="color:#0000ff">else if </span>(result != 0) { lastComparisonResult = result; } } <span style="color:#0000ff">return </span>lastComparisonResult; } <span style="color:#0000ff">private int </span>CompareConversion(<span style="color:#2b91af">TypeBase </span>leftTarget, <span style="color:#2b91af">TypeBase </span>rightTarget, <span style="color:#2b91af">Expression </span>source) { <span style="color:#0000ff">if </span>(leftTarget == rightTarget) { <span style="color:#008000">//same type, no better one </span><span style="color:#0000ff">return </span>0; } <span style="color:#0000ff">else if </span>(leftTarget == source.ExpressionType && rightTarget != source.ExpressionType) { <span style="color:#008000">//left is better; </span><span style="color:#0000ff">return </span>-1; } <span style="color:#0000ff">else if </span>(leftTarget != source.ExpressionType && rightTarget == source.ExpressionType) { <span style="color:#008000">//right is better; </span><span style="color:#0000ff">return </span>1; } <span style="color:#0000ff">else </span>{ <span style="color:#0000ff">if </span>(leftTarget.IsAssignableFrom(rightTarget)) { <span style="color:#008000">//right is more specific </span><span style="color:#0000ff">return </span>1; } <span style="color:#0000ff">else if</span>(rightTarget.IsAssignableFrom(leftTarget)) { <span style="color:#008000">//left is more specific </span><span style="color:#0000ff">return </span>-1; } <span style="color:#0000ff">else </span>{ <span style="color:#008000">//both are bad </span><span style="color:#0000ff">return </span>0; } } } } </span> |

最后,我们要将这一系列步骤组合到一起。由于miniSharp的类可以以任何顺序定义,一个类中的方法也可以以任何顺序定义,调用时并不受任何限制。所以我们无法只用一次抽象语法树的遍历来完成语义分析。我采用的做法是分成三次遍历,前两次分别对类的生命和成员的声明进行解析并构建符号表(类型和成员),第三次再对方法体进行解析。这样就可以方便地处理不同顺序定义的问题。总的来说,三次遍历的任务是:

  1. 第一遍:扫描所有class定义,检查有无重名的情况。
  2. 第二遍:检查类的基类是否存在,检测是否循环继承;检查所有字段的类型以及是否重名;检查所有方法参数和返回值的类型以及是否重复定义(签名完全一致的情况)。
  3. 第三遍:检查所有方法体中语句和表达式的语义。

因为上一次抽象语法树的设计已经采用了Visitor模式,所以以上三个阶段的语义分析可以分别写成三个Visitor来进行处理。语义分析模块同时还要报告所有语义错误。下面我给出第一阶段的Visitor实现供大家参考:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| code <span style="color:#000000"><span style="color:#0000ff">public class </span><span style="color:#2b91af">TypeDeclResolver </span>: <span style="color:#2b91af">AstVisitor </span>{ <span style="color:#0000ff">private </span><span style="color:#2b91af">TypeCollection </span>m_types; <span style="color:#0000ff">private </span><span style="color:#2b91af">CompilationErrorManager </span>m_errorManager; <span style="color:#0000ff">private const int </span>c_SE_TypeNameDuplicates = 301; <span style="color:#0000ff">public void </span>DefineErrors() { m_errorManager.DefineError(c_SE_TypeNameDuplicates, 0, <span style="color:#2b91af">CompilationStage</span>.SemanticAnalysis, <span style="color:#a31515">"The program has already defined a type named '{0}'."</span>); } <span style="color:#0000ff">public </span>TypeDeclResolver(<span style="color:#2b91af">CompilationErrorManager </span>errorManager) { m_errorManager = errorManager; m_types = <span style="color:#0000ff">new </span><span style="color:#2b91af">TypeCollection</span>(); } <span style="color:#0000ff">public override </span><span style="color:#2b91af">AstNode </span>VisitProgram(<span style="color:#2b91af">Program </span>ast) { Visit(ast.MainClass); <span style="color:#0000ff">foreach </span>(<span style="color:#0000ff">var </span>cd <span style="color:#0000ff">in </span>ast.Classes) { Visit(cd); } <span style="color:#0000ff">return </span>ast; } <span style="color:#0000ff">public override </span><span style="color:#2b91af">AstNode </span>VisitMainClass(<span style="color:#2b91af">MainClass </span>ast) { <span style="color:#008000">//main class must be the first class. </span><span style="color:#2b91af">Debug</span>.Assert(m_types.Count == 0); <span style="color:#0000ff">var </span>name = ast.Name.Value; <span style="color:#0000ff">var </span>mainclassType = <span style="color:#0000ff">new </span><span style="color:#2b91af">CodeClassType</span>() { Name = name, IsStatic = <span style="color:#0000ff">true </span>}; m_types.Add(mainclassType); ast.Type = mainclassType; <span style="color:#0000ff">return </span>ast; } <span style="color:#0000ff">public override </span><span style="color:#2b91af">AstNode </span>VisitClassDecl(<span style="color:#2b91af">ClassDecl </span>ast) { <span style="color:#0000ff">var </span>name = ast.Name.Value; <span style="color:#0000ff">if </span>(m_types.Contains(name)) { m_errorManager.AddError(c_SE_TypeNameDuplicates, ast.Name.Span, name); <span style="color:#0000ff">return </span>ast; }</span> |

相关推荐
wbs_scy3 小时前
Linux C++ 高并发编程:线程池全链路深度解析,从原理到手撕实现
java·开发语言
JAVA面经实录9173 小时前
Linux 常用命令完整知识体系
java·linux·开发语言·汇编
贪玩的蛋挞3 小时前
C#与闭包
开发语言·c#
端庄的战斗机4 小时前
javascript 设计模式(文章很长,请自备瓜子,水果和眼药水)
开发语言·javascript·设计模式
Full Stack Developme4 小时前
Java LRU 与 LFU 算法及应用
java·开发语言·算法
C语言小火车6 小时前
C++ 堆排序深度精讲:基于完全二叉树的选择排序进化,最坏情况 O(n log n) 的稳定王者
开发语言·c++·算法·排序算法·堆排序
北冥you鱼6 小时前
abigen 最佳实践:从入门到精通,高效生成 Go 语言合约绑定
开发语言·golang·区块链
程序员老油条6 小时前
WSL2 + Docker Desktop:Windows 下的完美 Java 开发环境
java·windows·docker·wsl2
ALex_zry7 小时前
C++26 std::complex 结构化绑定详解:auto [re, im] = c
c语言·开发语言·c++