chai2010 / go-ast-book

:books: 《Go语言定制指南》(原名:Go语法树入门/开源免费图书/Go语言进阶/掌握抽象语法树/Go语言AST)
https://chai2010.cn/go-ast-book
5.33k stars 653 forks source link

第13章静态单赋值形式135页 prog.infos 未定义 找不到代码定义的地方 #37

Open CheungChan opened 2 years ago

CheungChan commented 2 years ago

如题

chai2010 commented 2 years ago

这是需要自己定义的属性,完整的代码如下:

package main

import (
    "go/ast"
    "go/parser"
    "go/token"
    "go/types"
    "os"

    "golang.org/x/tools/go/ssa"
)

func main() {
    prog := NewProgram(map[string]string{
        "main": `
            package main
            func main() {
                for i := 0; i < 3; i++ {
                    println(i, "hello chai2010")
                }
            }
        `,
    })

    pkg, f, info, _ := prog.LoadPackage("main")

    var ssaProg = ssa.NewProgram(prog.fset, ssa.SanityCheckFunctions)
    var ssaPkg = ssaProg.CreatePackage(pkg, []*ast.File{f}, info, true)
    ssaPkg.Build()

    ssaPkg.WriteTo(os.Stdout)

    ssaPkg.Func("main").WriteTo(os.Stdout)
}

type Program struct {
    fs    map[string]string
    ast   map[string]*ast.File
    pkgs  map[string]*types.Package
    infos map[string]*types.Info
    fset  *token.FileSet
}

func NewProgram(fs map[string]string) *Program {
    return &Program{
        fs:    fs,
        ast:   make(map[string]*ast.File),
        pkgs:  make(map[string]*types.Package),
        infos: make(map[string]*types.Info),
        fset:  token.NewFileSet(),
    }
}

func (p *Program) LoadPackage(path string) (pkg *types.Package, f *ast.File, info *types.Info, err error) {
    if pkg, ok := p.pkgs[path]; ok {
        return pkg, p.ast[path], p.infos[path], nil
    }

    f, err = parser.ParseFile(p.fset, path, p.fs[path], parser.AllErrors)
    if err != nil {
        return nil, nil, nil, err
    }

    info = &types.Info{
        Types:      make(map[ast.Expr]types.TypeAndValue),
        Defs:       make(map[*ast.Ident]types.Object),
        Uses:       make(map[*ast.Ident]types.Object),
        Implicits:  make(map[ast.Node]types.Object),
        Selections: make(map[*ast.SelectorExpr]*types.Selection),
        Scopes:     make(map[ast.Node]*types.Scope),
    }

    conf := types.Config{Importer: p}
    pkg, err = conf.Check(path, p.fset, []*ast.File{f}, info)
    if err != nil {
        return nil, nil, nil, err
    }

    p.ast[path] = f
    p.pkgs[path] = pkg
    p.infos[path] = info
    return pkg, f, info, nil
}

func (p *Program) Import(path string) (*types.Package, error) {
    if pkg, ok := p.pkgs[path]; ok {
        return pkg, nil
    }
    pkg, _, _, err := p.LoadPackage(path)
    return pkg, err
}
CheungChan commented 2 years ago

我看书里后面有 exitCode := interp.Interpret(ssaPkg, 0, &types.StdSizes{8, 8}, "main", []string{}) if exitCode != 0 { fmt.Println("exitCode:", exitCode) } 这个放在后面执行会报错 panic: ssa.Program doesn't include runtime package

chai2010 commented 2 years ago
package main

import (
    "fmt"
    "go/ast"
    "go/parser"
    "go/token"
    "go/types"

    "golang.org/x/tools/go/ssa"
    "golang.org/x/tools/go/ssa/interp"
)

func main() {
    prog := NewProgram(map[string]string{
        "main": `
            package main

            func main() {
                for i := 0; i < 3; i++ {
                    println(i, "hello chai2010")
                }
            }
        `,
        "runtime": `
            package runtime

            type errorString string

            func (e errorString) RuntimeError() {}
            func (e errorString) Error() string { return "runtime error: " + string(e) }

            type Error interface {
                error
                RuntimeError()
            }
        `,
    })

    prog.LoadPackage("main")
    prog.LoadPackage("runtime")

    var ssaProg = ssa.NewProgram(prog.fset, ssa.SanityCheckFunctions)
    var ssaMainPkg *ssa.Package

    for name, pkg := range prog.pkgs {
        ssaPkg := ssaProg.CreatePackage(pkg, []*ast.File{prog.ast[name]}, prog.infos[name], true)
        if name == "main" {
            ssaMainPkg = ssaPkg
        }
    }
    ssaProg.Build()

    exitCode := interp.Interpret(
        ssaMainPkg, 0, &types.StdSizes{8, 8},
        "main", []string{},
    )
    if exitCode != 0 {
        fmt.Println("exitCode:", exitCode)
    }
}

type Program struct {
    fs    map[string]string
    ast   map[string]*ast.File
    pkgs  map[string]*types.Package
    infos map[string]*types.Info
    fset  *token.FileSet
}

func NewProgram(fs map[string]string) *Program {
    return &Program{
        fs:    fs,
        ast:   make(map[string]*ast.File),
        pkgs:  make(map[string]*types.Package),
        infos: make(map[string]*types.Info),
        fset:  token.NewFileSet(),
    }
}

func (p *Program) LoadPackage(path string) (pkg *types.Package, f *ast.File, err error) {
    if pkg, ok := p.pkgs[path]; ok {
        return pkg, p.ast[path], nil
    }

    f, err = parser.ParseFile(p.fset, path, p.fs[path], parser.AllErrors)
    if err != nil {
        return nil, nil, err
    }

    info := &types.Info{
        Types:      make(map[ast.Expr]types.TypeAndValue),
        Defs:       make(map[*ast.Ident]types.Object),
        Uses:       make(map[*ast.Ident]types.Object),
        Implicits:  make(map[ast.Node]types.Object),
        Selections: make(map[*ast.SelectorExpr]*types.Selection),
        Scopes:     make(map[ast.Node]*types.Scope),
    }

    conf := types.Config{Importer: p}
    pkg, err = conf.Check(path, p.fset, []*ast.File{f}, info)
    if err != nil {
        return nil, nil, err
    }

    p.ast[path] = f
    p.pkgs[path] = pkg
    p.infos[path] = info
    return pkg, f, nil
}

func (p *Program) Import(path string) (*types.Package, error) {
    if pkg, ok := p.pkgs[path]; ok {
        return pkg, nil
    }
    pkg, _, err := p.LoadPackage(path)
    return pkg, err
}
chai2010 commented 2 years ago

代码在每章的 examples 目录:https://github.com/chai2010/go-ast-book/tree/master/ch13/examples