为了让只有非本质差异的解答拥有相同的表示,表示器应该应用规范化。一般来说,创建规范化表示的过程如下:
representation.txt的文件(参见接口)不过要注意,表示并不_一定_非得是 AST,它也可以是规范化后的普通代码,哪种最适合你的学习路径就用哪种。
为了帮你上手,下面我们来介绍一些常见的规范化策略。
注意 1:这些规范化示例之间没有递进关系,每个示例只展示一种具体的规范化。真正的表示器会希望依次应用它们。
注意 2:这些指南中的代码将使用 C# 编写,但指南本身与具体语言无关。
为了让表示与命名无关,用户自定义的名称(例如变量、函数等)可以替换为占位符。这种情况下,应该生成一个mapping.json(参见接口)。
需要注意的是,所有相同的名称都必须替换为同一个占位符,无论它们位于哪个作用域。
public static class Fake
{
public static int Test(int input)
{
var test = input + 2;
return test;
}
}
public static class PLACEHOLDER_1
{
public static int PLACEHOLDER_2(int PLACEHOLDER_3)
{
var PLACEHOLDER_4 = PLACEHOLDER_3 + 2;
return PLACEHOLDER_4;
}
}
空白不一致的情况非常常见,因此规范化空白是很常见的一个规范化步骤。 行尾符也应该规范化。
using System;
public static class Fake
{
public static DateTime Add (DateTime birthDate)
{
return birthDate.Add( TimeSpan.FromSeconds ( 10 ) ) ;
}
}
public static class Fake
{
public static DateTime Add(DateTime birthDate)
{
return birthDate.Add(TimeSpan.FromSeconds(10));
}
}
在很多语言中,用户可以自行决定如何定义代码块(或作用域)。例如,在大多数类 C 语言中,作用域是用大括号括起来的。通常,把大括号放在同一行还是下一行都无所谓,因此可以对此进行规范化。
public static class Fake {
public static int Test() {
if (1 > 2) {
return 0;
}
return 1;
}
}
public static class Fake
{
public static int Test()
{
if (1 > 2)
{
return 0;
}
return 1;
}
}
并非所有代码片段对表示器来说都有意义,这些片段可以被移除。举例来说,在大多数语言中,注释都无关紧要,可以安全地移除。
/*
These are some very nice
comments spanning multiple lines
*/
public static class Fake
{
// Nice method
public static string Test()
{
return "Test"; // This is very nice
}
}
public static class Fake
{
public static string Test()
{
return "Test";
}
}
在某些情况下,代码的顺序无关紧要。为了防止同样的代码因顺序不同而产生不同的表示,对它进行排序可能会有用。通常需要排序的是函数或声明。找到排序所依据的衡量标准可能很棘手,实现起来也同样棘手。一种衡量标准可以是节点包含多少个 AST 子节点,另一种可以是第一个子节点的类型名称。
这个示例按照某种函数长度进行排序:
public static class Fake
{
public static string Test2()
{
int a = "Test2";
return a;
}
public static string Test()
{
return "Test";
}
}
public static class Fake
{
public static string Test()
{
return "Test";
}
public static string Test2()
{
int a = "Test2";
return a;
}
}