Go关键字的一次汉化过程

1. 为什么写本文

笔者在自己的原创文章 [关于中文编程的一些思考]中,有一段关于go的中文代码,并且还能正常执行。

可能有些小伙伴看到之后,在自己的电脑上也执行了同样的代码却报错,原因就是:还需要对部分源码进行修改。这里,笔者就整理了一下操作的步骤。

2. 源码修改步骤

拉取Go源代码

1
git clone https://github.com/golang/go.git

从GitHub拉取Go源代码的好处是:拉取的源码中包含了多个版本分支,可以切换到自己想要的版本。

切换到编译的版本

1
2
3
cd go
git branch -a
git checkout -b go1.14 origin/dev.boringcrypto.go1.14

笔者这里是在go1.14的基础上进行的关键字汉化,你可以选择自己想要的版本。

添加中文关键字

文件:go\src\go\token\token.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 添加中文关键字数组
// 这里笔者仅汉化了IF ELSE
var tokens_cn = [...]string{
BREAK: "break",
CASE: "case",
CHAN: "chan",
CONST: "const",
CONTINUE: "continue",

DEFAULT: "default",
DEFER: "defer",
ELSE: "否则",
FALLTHROUGH: "fallthrough",
FOR: "for",

FUNC: "func",
GO: "go",
GOTO: "goto",
IF: "如果",
IMPORT: "import",

INTERFACE: "interface",
MAP: "map",
PACKAGE: "package",
RANGE: "range",
RETURN: "return",

SELECT: "select",
STRUCT: "struct",
SWITCH: "switch",
TYPE: "type",
VAR: "var",
}

func init() {
keywords = make(map[string]Token)
for i := keyword_beg + 1; i < keyword_end; i++ {
keywords[tokens[i]] = i
keywords[tokens_cn[i]] = i // 关键!把自定义的中文关键字添加进来
}
}

笔者这里仅汉化了IF/ELSE,如果想汉化其他的关键字可以在tokens_cn数组进行修改即可。

文件:go\src\cmd\compile\internal\syntax\scanner.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 添加token识别函数
// 笔者这里仅修改了 if else
func getCnToken(lit []byte) token {
switchstring(lit) {
case"break": return _Break
case"case": return _Case
case"chan": return _Chan
case"const": return _Const
case"continue": return _Continue
case"default": return _Default
case"defer": return _Defer
case"否则": return _Else
case"fallthrough": return _Fallthrough
case"for": return _For
case"func": return _Func
case"go": return _Go
case"goto": return _Goto
case"如果": return _If
case"import": return _Import
case"interface": return _Interface
case"map": return _Map
case"package": return _Package
case"range": return _Range
case"return": return _Return
case"select": return _Select
case"struct": return _Struct
case"switch": return _Switch
case"type": return _Type
case"var": return _Var
default: return0
}
}

func (s *scanner) ident() {
s.startLit()

// accelerate common case (7bit ASCII)
c := s.getr()
for isLetter(c) || isDecimal(c) {
c = s.getr()
}

// general case
if c >= utf8.RuneSelf {
for s.isIdentRune(c, false) {
c = s.getr()
}
}
s.ungetr()

lit := s.stopLit()

// possibly a keyword
iflen(lit) >= 2 {
if tok := keywordMap[hash(lit)]; tok != 0 && tokStrFast(tok) == string(lit) {
s.nlsemi = contains(1<<_Break|1<<_Continue|1<<_Fallthrough|1<<_Return, tok)
s.tok = tok
return
} elseif tok := getCnToken(lit); tok != 0 { // 添加else if,识别出正确的中文token。共5行代码
s.nlsemi = contains(1<<_Break|1<<_Continue|1<<_Fallthrough|1<<_Return, tok)
s.tok = tok
return
}
}

s.nlsemi = true
s.lit = string(lit)
s.tok = _Name
}

添加中文函数关键字

文件 go\src\cmd\compile\internal\gc\universe.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var builtinFuncs = [...]struct {
name string
op Op
}{
{"append", OAPPEND},
{"cap", OCAP},
{"close", OCLOSE},
{"complex", OCOMPLEX},
{"copy", OCOPY},
{"delete", ODELETE},
{"imag", OIMAG},
{"len", OLEN},
{"make", OMAKE},
{"new", ONEW},
{"panic", OPANIC},
{"print", OPRINT},
{"输出", OPRINT}, // 对函数print进行汉化
{"println", OPRINTN},
{"real", OREAL},
{"recover", ORECOVER},
}

笔者这里只汉化了一个内置函数print。

编译源码

1
./all.bash

到这里,go源码的修改已经可以支持笔者示例中的中文代码。这里只是抛砖引玉,其他操作有待你的发掘。

这一步有2件事情需要注意:

  • 执行 ./all.bash时,必须进到go源码下的src目录执行
  • 如果没有安装其他版本的go程序,会有报错信息提示安装go1.4

3. 汉化验证

示例代码 demo.go

1
2
3
4
5
6
7
8
9
package main
func main(){
如果 (1==1) {
输出("这里是如果");
} 否则 {
输出("这里是否则");
}
}
go run demo.go

结果具体是什么?是否跟笔者的一致?聪明的你试试便知。

4. 总结

常见的汉化一般有:国外软件汉化、操作系统汉化、APP汉化等,而对编程语言的汉化则不常见。

本文的一系列操作,只想让你明白:编程语言在编译过程中有词法分析、语法分析的步骤 (笔者的另一篇文章:[关于中文编程的一些思考]中有写)。了解其工作原理,能够对编程语言有更深层次的理解提升。

5.参考

[1] https://zhuanlan.zhihu.com/p/106104002 给go语言添加中文关键字