[求助]如何在Golang中修改两个scanner.go文件以支持Allman风格的大括号

[求助]如何在Golang中修改两个scanner.go文件以支持Allman风格的大括号 我已修改了Golang引擎的13行代码以支持Allman风格:https://github.com/forkgo-org/go/blob/master/src/cmd/compile/internal/syntax/scanner.go#L119

(https://github.com/golang/go/compare/master...forkgo-org:masterdiff-8a3eef071ab7276287722ebb98acdb3521c251d2857d687cae762e394d1710e4)

我最终生成的Go二进制文件可以成功运行和构建Allman风格的Go文件, 看起来一切正常, 但我不知道这个文件的用途:github/forkgo-org/go/blob/master/src/go/scanner/scanner.go#L864 而且我不知道如何修改它以保持与以下文件的一致性:github/forkgo-org/go/blob/master/src/cmd/compile/internal/syntax/scanner.go#L119

为什么有两个scanner.go文件?我们可以将它们合并成一个吗?

抱歉分叉了Golang,请轻点批评。

欢迎帮助我修复github/forkgo-org/go/blob/master/src/go/scanner/scanner.go#L864


更多关于[求助]如何在Golang中修改两个scanner.go文件以支持Allman风格的大括号的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

谢谢,现在我明白了 go/scanner 是用于处理 Go 文件的。

目前,forkgo 是我的个人兴趣项目,也许等我熟练掌握后,我希望它能成为对 Golang 的一个有价值的补充。

更多关于[求助]如何在Golang中修改两个scanner.go文件以支持Allman风格的大括号的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我已经修复了 go/scanner/scanner.go(https://github.com/forkgo-org/go/blob/master/src/go/scanner/scanner.go#L869),现在它与 cmd/compile/internal/syntax/scanner.go 保持一致。

看起来现在一切正常,试试 forkgo:https://github.com/forkgo-org/go

你的分叉行为违背了我认为是Go语言核心原则之一:它对代码格式有明确的约定。Go之所以对格式有明确约定,正是为了避免团队之间就Allman与K&R风格这类问题争论不休。我过去曾参与过团队中的此类辩论,这总是浪费时间。因此,我敢打赌,你可能不会得到太多支持。

此外,如果你在修改编译器却不理解你正在修改的代码,在我看来这似乎是个糟糕的主意。快速浏览相关包后,在编译器介绍中发现了这段注释:

请注意,go/* 系列的包,例如 go/parsergo/types,与编译器无关。由于编译器最初是用C语言编写的,go/* 包是为了支持用Go编写处理Go代码的工具(如 gofmtvet)而开发的。

……这解释了为什么会有两种实现。go/* 是为了支持用Go编写的工具而构建的,与编译器无关。

在你的分叉项目运行后,你在团队协作中将会遇到重大问题。例如,我的编辑器在保存文件时会运行 go fmt。如果你的团队/协作者没有运行你的分叉版本,那么他们每次保存都会覆盖你文件中的格式。这只是一个学习练习/个人项目吗?

在Go语言中确实存在两个不同的scanner.go文件,它们服务于不同的编译阶段和工具链组件:

1. 两个scanner.go文件的区别

src/cmd/compile/internal/syntax/scanner.go

  • 用于Go编译器前端(cmd/compile
  • 处理Go源代码的词法分析
  • 直接集成在编译器内部

src/go/scanner/scanner.go

  • 用于go/*包族(如go/parser, go/ast等)
  • 提供给外部工具使用的词法分析器
  • gofmt, goimports, gopls等工具使用

2. 修改go/scanner/scanner.go以支持Allman风格

根据你已修改的编译器scanner,需要同步修改go/scanner/scanner.go的第864行附近。以下是具体的修改示例:

// 在 go/scanner/scanner.go 中查找并修改
func (s *Scanner) scanComment() string {
    // ... 现有代码 ...
    
    // 大约在第864行附近,修改大括号处理逻辑
    case '{':
        if s.mode&ScanComments == 0 {
            // 修改前:直接返回
            // 修改后:检查是否在特定上下文中需要换行
            if s.allmanStyle && s.prevToken != token.LBRACE {
                // 为Allman风格添加额外处理
                s.lineBreak = true
            }
            s.tok = token.LBRACE
            s.lit = "{"
            s.offset = s.offset0
            s.ch = s.next()
            return
        }
    // ... 其他case处理 ...
}

3. 完整的同步修改示例

// 在 Scanner 结构体中添加 Allman 风格支持
type Scanner struct {
    // ... 现有字段 ...
    allmanStyle bool // 添加此字段
}

// 修改初始化函数
func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
    // ... 现有初始化代码 ...
    s.allmanStyle = false // 默认值,可通过配置修改
}

// 修改大括号扫描逻辑
func (s *Scanner) scanToken() {
    // ... 现有代码 ...
    
    switch ch {
    case '{':
        if s.mode&ScanComments == 0 {
            // Allman风格支持:在前一个token不是左大括号时添加换行提示
            if s.allmanStyle && s.prevToken != token.LBRACE {
                s.insertSemi = true // 提示需要换行
            }
            s.tok = token.LBRACE
            s.lit = "{"
            s.offset = s.offset0
            s.ch = s.next()
            return
        }
        
    case '}':
        if s.mode&ScanComments == 0 {
            // Allman风格支持:右大括号处理
            if s.allmanStyle {
                s.insertSemi = true // 提示需要换行
            }
            s.tok = token.RBRACE
            s.lit = "}"
            s.offset = s.offset0
            s.ch = s.next()
            return
        }
    }
    // ... 其他case处理 ...
}

4. 为什么不能合并两个scanner

两个scanner不能合并的原因:

  • 职责分离:编译器scanner专注于编译效率,go/scanner专注于工具链API稳定性
  • 依赖关系go/scanner不能依赖编译器内部包
  • API设计go/scanner作为公开API需要保持向后兼容
  • 性能考虑:编译器scanner可以针对编译优化,而工具scanner需要更灵活的配置

5. 测试修改是否生效

// 测试代码示例
package main

import (
    "fmt"
    "go/scanner"
    "go/token"
)

func main() {
    src := []byte(`package main

func main()
{
    fmt.Println("Allman style")
}`)
    
    var s scanner.Scanner
    fset := token.NewFileSet()
    file := fset.AddFile("test.go", fset.Base(), len(src))
    
    // 需要扩展Scanner以支持Allman模式
    s.Init(file, src, nil, scanner.ScanComments)
    
    // 扫描tokens
    for {
        pos, tok, lit := s.Scan()
        if tok == token.EOF {
            break
        }
        fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
    }
}

两个scanner必须保持同步修改,否则会出现工具链(如gofmt、IDE插件)与编译器行为不一致的问题。建议通过构建测试确保两者都正确处理Allman风格的大括号。

回到顶部