Go constant.MakeFromLiteral 怎样按 token 种类解析常量
我在给 Go 源码分析器补“常量值”这一层时,最容易写错的不是 constant.MakeFromLiteral 的调用,而是把字面量文本和错误的 token.Token 配在一起。这个函数并不会替你猜类型:"42" 传成 token.INT 才会按整数解析,"42" 传成 token.FLOAT 就是另一条语义路径。
这次我把它整理成一条稳定的工作流:扫描阶段保留原始文本和 token,转换阶段只接收五种合法字面量 token,失败时检查 Value.Kind() 是否为 constant.Unknown。这样既能覆盖整数、浮点数、虚数、字符和字符串,也不会把非法输入悄悄当成零值。
- 输入:原始字面量字符串、对应的
token.Token、固定为0的精度参数。 - 分支:
token.INT、token.FLOAT、token.IMAG、token.CHAR、token.STRING。 - 失败判断:字面量语法非法时返回
Unknown,不要用String()是否为空来猜。
我第一次踩坑:literal 和 token 不是一回事
lit 是源码里看到的那段文字,tok 是词法分析器对这段文字给出的类别。它们必须来自同一个扫描结果,不能先拿字符串再凭经验补 token。官方 go/token 将 INT、FLOAT、IMAG、CHAR、STRING 定义为基础字面量 token;constant.MakeFromLiteral 也只接受这五类。

我现在会把这两个字段放在同一个结构里传递,避免后面的阶段重新推断:
package main
import (
"fmt"
"go/constant"
"go/token"
)
type literalInput struct {
text string // 保留源码中的原始字面量,包括引号或虚数后缀
kind token.Token // 必须与 text 来自同一次词法扫描
}
func valueOf(in literalInput) constant.Value {
// zero 必须传 0;当前 API 不用它指定额外精度
return constant.MakeFromLiteral(in.text, in.kind, 0)
}
func main() {
in := literalInput{text: "0x2a", kind: token.INT}
value := valueOf(in)
fmt.Println(value.Kind(), value.ExactString())
}
这里没有把 0x2a 先转成 Go 的 int。保留原文很重要,因为 MakeFromLiteral 负责按 Go 字面量语法解释前缀、后缀和引号;提前转成机器整数反而会丢掉源码信息。
按五类 token 接入转换阶段
实际接入时,我不再写一串“看起来像数字”的判断,而是让扫描结果直接驱动转换。token.INT 覆盖十进制、八进制、十六进制等整数写法;token.FLOAT 交给浮点字面量解析;token.IMAG 保留虚数后缀;CHAR 和 STRING 则保持 Go 的引号与转义规则。
func parseLiteral(text string, kind token.Token) (constant.Value, error) {
// 只允许 MakeFromLiteral 文档规定的五类字面量
switch kind {
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
value := constant.MakeFromLiteral(text, kind, 0)
// Unknown 表示语法或数值无法表示,不能当成合法零值
if value.Kind() == constant.Unknown {
return value, fmt.Errorf("invalid %s literal %q", kind, text)
}
return value, nil
default:
return constant.MakeUnknown(), fmt.Errorf("token %s is not a literal", kind)
}
}
这个分支有一个实际收益:标识符、关键字和运算符不会误进常量转换。对于字符串和字符,我也不会调用 Int64Val 之类的数值提取函数,而是根据 Kind 选择 StringVal 或精确字符串表示。

把扫描结果原样交给 MakeFromLiteral
如果字面量来自源文件,推荐让 go/scanner 产出 token 和原始文本。示例只展示接线方式,重点是 token.Position、token 类别和文本在同一处被记录,后续阶段不需要重新切片猜测。
func collectLiterals(src string) []literalInput {
// File 记录源码范围,scanner 负责同时给出 token 和原始文本
file := token.NewFileSet().AddFile("input.go", -1, len(src))
var s scanner.Scanner
s.Init(&file, []byte(src), nil, scanner.ScanComments)
var result []literalInput
for {
_, kind, text := s.Scan()
// EOF 结束扫描;其余 token 只收集 MakeFromLiteral 支持的类别
if kind == token.EOF {
break
}
if kind.IsLiteral() && kind != token.IDENT {
result = append(result, literalInput{text: text, kind: kind})
}
}
return result
}
工程代码中要补上 go/scanner 导入,并按需要处理注释;这里刻意没有把扫描输出说成“已运行结果”,它只是把数据边界放在正确的位置。若上游已经有 AST 或类型检查结果,也应优先复用已有 token 和原文。
性能上的取舍:一次解析,后面复用 Value
我最初在多个规则里重复调用 MakeFromLiteral,在大文件分析时会让同一个字面量反复走解析分支。后来把 literalInput 转成 constant.Value 放在节点级缓存里:规则需要判断大小、类型或拼接时复用 Value,只有源码文本变化才重新建立。
这里的“性能优化”不是把精确常量换成 float64。go/constant 表示的是无类型 Go 常量,过早转成机器类型可能丢失精度。更稳妥的顺序是先保留 constant.Value,在真正需要生成运行时参数时,再调用对应的 Int64Val、Float64Val 或 StringVal 并处理返回的可表示性。
| 源码字面量 | token | Value 方向 | 注意点 |
|---|---|---|---|
0x2a | token.INT | Int | 保留前缀,不先转机器整数 |
3.14 | token.FLOAT | Float | 不要拿 Int 分支代替 |
2i | token.IMAG | Complex | 保留 i 后缀 |
'中' | token.CHAR | Int | 按字符字面量规则解码 |
"go" | token.STRING | String | 需要 StringVal 才取无引号文本 |
常见误区和我的收尾清单
第一,不要把第三个参数写成自定义精度;当前签名要求它为零。第二,不要只看 Value.String() 判断成功,应该检查 Kind(),字符串还要区分展示形式和实际内容。第三,不要在非法 token 上硬调函数,先做白名单分支。第四,源码分析器要保留位置和原文,这样出现 Unknown 时还能把错误指回具体字面量。
对我来说,最可靠的收尾方式是:扫描时记录 (text, token),转换时只进五类白名单,结果层检查 Unknown,业务层再决定是否转成机器类型。这样 constant.MakeFromLiteral 就是清晰的转换边界,而不是一个需要猜参数的黑盒。
常见问题
为什么不能只传字符串让 MakeFromLiteral 自动判断?
因为函数签名要求显式的 token.Token。同一段文本在不同 token 下代表不同语义,正确做法是复用词法扫描阶段产出的类别。
第三个参数为什么固定传 0?
当前 API 的参数名是 zero,文档要求最终参数为零。不要把它当成浮点精度开关。
解析失败后为什么不直接返回 nil?
constant.Value 使用 Unknown 表示错误或未知值。调用方应先检查 Kind(),再决定记录诊断、跳过规则或返回错误。
Go 非法字面量为什么返回 Unknown 而不是普通 error
- 上一篇
- Go 非法字面量为什么返回 Unknown 而不是普通 error
- 下一篇
- Go comment.Printer 怎样把结构化注释转换为 Markdown
-
- Golang · Go教程 | 32分钟前 |
- Go constant.MakeImag 怎样组装精确复数常量
- 111浏览 收藏
-
- Golang · Go教程 | 36分钟前 | 源码分析 · Go教程 · Go 大整数 位移 go/constant constant.Shift
- Go constant.Shift 怎样处理超出机器字长的整数位移
- 481浏览 收藏
-
- Golang · Go教程 | 40分钟前 |
- Go constant.BinaryOp 怎样对分数字面量做精确运算
- 300浏览 收藏
-
- Golang · Go教程 | 48分钟前 |
- Go token.FileSet 怎样通过 Read 与 Write 保存位置映射
- 430浏览 收藏
-
- Golang · Go教程 | 49分钟前 |
- Go 长驻分析工具怎样从 FileSet 移除不再使用的文件
- 393浏览 收藏
-
- Golang · Go教程 | 53分钟前 |
- Go token.FileSet.Iterate 怎样导出多文件的基址范围
- 462浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go token.File.Pos 怎样从字节偏移取得源码位置
- 105浏览 收藏
-
- Golang · Go教程 | 1小时前 | Go FileSet 源码位置 token.File SetLinesForContent
- Go token.File.SetLinesForContent 怎样为内存源码建立行表
- 368浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go 修改 ImportSpec 时怎样保留尾部导入注释
- 180浏览 收藏
-
- Golang · Go教程 | 1小时前 | Go 导入路径 ImportSpec.Path strconv.Unquote
- Go ImportSpec.Path 怎样还原成没有引号的导入路径
- 153浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · 源码分析 · go/parser go/token Go AST ast.ImportSpec
- Go AST 怎样列出导入路径及显式别名
- 319浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 968次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 977次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1085次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1061次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 899次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览

