Go 1.20+ 安装后 go 命令不可用是因 shell 未加载新 PATH:macOS/Linux 需将 PATH 加入 ~/.zshrc 并 source,Windows 需勾选“Add Go to environment variables”或手动添加 C:\Go\bin;go.mod 必须在含 main.go 的项目根目录立即初始化,模块名须唯一;text/scanner 不支持嵌套注释,需手动处理;AST 应先用具体 struct 硬编码,再逐步抽象,优先保证合法输入正确解析。

Go 1.20+ 安装后 go 命令不可用?检查 PATH 和 shell 初始化
不是没装成功,而是 shell 没读到新 PATH。macOS/Linux 下常见于用了 zsh 却把 PATH 加在 ~/.bash_profile 里;Windows 则常因未勾选“Add Go to PATH”或安装后没重启终端。
- macOS:确认
go装在/usr/local/go,然后在~/.zshrc末尾加export PATH=$PATH:/usr/local/go/bin,再运行source ~/.zshrc - Windows:打开 cmd 运行
where go,若无输出,重装时务必勾选 “Add Go to environment variables”,或手动把C:\Go\bin加进系统环境变量 - 验证:执行
go version和go env GOPATH,后者应返回非空路径(如$HOME/go)
写个词法分析器前先搞清 go.mod 初始化时机
别等写完 main.go 再跑 go mod init——那样会导致导入路径错误、go run 找不到包。必须在项目根目录下,且 main.go 存在时立即初始化。
- 正确顺序:
mkdir mycompiler && cd mycompiler && go mod init mycompiler && touch main.go - 如果已乱序操作,删掉自动生成的
go.sum和错误的go.mod,再重新go mod init -
go.mod第一行模块名要能唯一标识项目,别用main或test,否则后续导入其他包时会冲突
用 text/scanner 做基础 token 提取,但别指望它处理嵌套注释
text/scanner 对单行 // 和块注释 /* */ 支持有限:它把 /* 当作两个独立字符,不识别成一个 token;遇到嵌套 /* /* */ */ 会直接 panic。
- 简单场景(如教学用计算器语法)可用:
var s scanner.Scanner; s.Init(strings.NewReader("x := 1 + 2")); for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { fmt.Printf("%s: %s\n", scanner.TokenString(tok), s.TokenText()) } - 真实编译器必须自己实现注释跳过逻辑——在扫描循环中检测
/后跟*或/,然后手动 consume 字符直到匹配结束 - 注意
scanner.Scan()返回的是token类型常量(如scanner.Ident),不是字符串,别直接fmt.Println(tok)
生成 AST 时避免过早抽象,先用 struct 硬编码节点类型
新手常一上来就设计泛型 Node interface{} 和一堆实现,结果类型断言满天飞、调试困难。不如从最简结构开始,比如只支持二元表达式:
立即学习“go语言免费学习笔记(深入)”;
type BinaryExpr struct {
Op token.Token
Left, Right Expr
}
type NumberLit struct {
Value float64
}- 先让
ParseExpr()能返回具体类型(如*BinaryExpr),而不是Expr接口,降低初期心智负担 - 等 parser 能稳定产出树形结构后,再引入接口和 visitor 模式,否则容易陷入“设计先行,跑不通代码”的陷阱
- 别忘了给每个 struct 加
String()方法,调试时fmt.Printf("%+v", ast)才有意义
AST 构建最难的不是语法,是错误恢复——一旦某个 token 错了,后面全乱。先保证合法输入能出正确树,再考虑报错提示,更别提自动修正。


















