Skip to content

Commit

Permalink
实现 embed 功能, 仅针对全局的 string 变量
Browse files Browse the repository at this point in the history
  • Loading branch information
chai2010 committed Apr 17, 2024
1 parent 2da40e0 commit 1a156e3
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 35 deletions.
19 changes: 10 additions & 9 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,15 +928,16 @@ func (*FuncDecl) declNode() {}
// and Comment comments directly associated with nodes, the remaining comments
// are "free-floating" (see also issues #18593, #20744).
type File struct {
Shebang string // `#!` 第一行开头的 Shebang 模式注释
Doc *CommentGroup // associated documentation; or nil
Package token.Pos // position of "package" keyword
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope (this file only)
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file
Comments []*CommentGroup // list of all comments in the source file
Shebang string // `#!` 第一行开头的 Shebang 模式注释
Doc *CommentGroup // associated documentation; or nil
Package token.Pos // position of "package" keyword
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope (this file only)
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file
Comments []*CommentGroup // list of all comments in the source file
EmbedMap map[string]string // #wa:embed 数据
}

func (f *File) Pos() token.Pos { return f.Package }
Expand Down
6 changes: 6 additions & 0 deletions internal/ast/astutil/comment_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type CommentInfo struct {
WasmModule string // #wa:wasm-module xxx
Generic []string // #wa:generic xxx yyy
Operator [][]string // #wa:operator + xxx yyy
Embed string // #wa:embed filename
}

// 获取节点关联文档
Expand Down Expand Up @@ -100,6 +101,11 @@ func ParseCommentInfo(docList ...*ast.CommentGroup) (info CommentInfo) {
info.Generic = append(info.Generic, parts[1:]...)
case "#wa:operator":
info.Operator = append(info.Operator, parts[1:])

case "#wa:embed":
if len(parts) >= 2 {
info.Embed = parts[1]
}
}
}
}
Expand Down
11 changes: 1 addition & 10 deletions internal/ast/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func exportFilter(name string) bool {
// stripped. The File.Comments list is not changed.
//
// FileExports reports whether there are exported declarations.
//
func FileExports(src *File) bool {
return filterFile(src, exportFilter, true)
}
Expand All @@ -36,7 +35,6 @@ func FileExports(src *File) bool {
//
// PackageExports reports whether there are exported declarations;
// it returns false otherwise.
//
func PackageExports(pkg *Package) bool {
return filterPackage(pkg, exportFilter, true)
}
Expand All @@ -60,7 +58,6 @@ func filterIdentList(list []*Ident, f Filter) []*Ident {
// fieldName assumes that x is the type of an anonymous field and
// returns the corresponding field name. If x is not an acceptable
// anonymous field, the result is nil.
//
func fieldName(x Expr) *Ident {
switch t := x.(type) {
case *Ident:
Expand Down Expand Up @@ -228,7 +225,6 @@ func filterSpecList(list []Spec, f Filter, export bool) []Spec {
//
// FilterDecl reports whether there are any declared names left after
// filtering.
//
func FilterDecl(decl Decl, f Filter) bool {
return filterDecl(decl, f, false)
}
Expand All @@ -253,7 +249,6 @@ func filterDecl(decl Decl, f Filter, export bool) bool {
//
// FilterFile reports whether there are any top-level declarations
// left after filtering.
//
func FilterFile(src *File, f Filter) bool {
return filterFile(src, f, false)
}
Expand All @@ -280,7 +275,6 @@ func filterFile(src *File, f Filter, export bool) bool {
//
// FilterPackage reports whether there are any top-level declarations
// left after filtering.
//
func FilterPackage(pkg *Package, f Filter) bool {
return filterPackage(pkg, f, false)
}
Expand Down Expand Up @@ -314,7 +308,6 @@ const (
// nameOf returns the function (foo) or method name (foo.bar) for
// the given function declaration. If the AST is incorrect for the
// receiver, it assumes a function instead.
//
func nameOf(f *FuncDecl) string {
if r := f.Recv; r != nil && len(r.List) == 1 {
// looks like a correct receiver declaration
Expand All @@ -334,12 +327,10 @@ func nameOf(f *FuncDecl) string {

// separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group
//
var separator = &Comment{token.NoPos, "//"}

// MergePackageFiles creates a file AST by merging the ASTs of the
// files belonging to a package. The mode flags control merging behavior.
//
func MergePackageFiles(pkg *Package, mode MergeMode) *File {
// Count the number of package docs, comments and declarations across
// all package files. Also, compute sorted list of filenames, so that
Expand Down Expand Up @@ -489,5 +480,5 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
}

// TODO(gri) need to compute unresolved identifiers!
return &File{"", doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
return &File{"", doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments, nil}
}
26 changes: 26 additions & 0 deletions internal/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"

"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/ast/astutil"
"wa-lang.org/wa/internal/config"
wzparser "wa-lang.org/wa/internal/frontend/wz/parser"
"wa-lang.org/wa/internal/loader/buildtag"
Expand Down Expand Up @@ -517,6 +518,7 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil
logger.Tracef(&config.EnableTrace_loader, "pkgpath: %v", pkgpath)

var (
pkgVFS fs.FS
extNames = []string{".wa", ".wz", ".wa.go"}
unitTestMode bool = false
datas [][]byte
Expand All @@ -529,6 +531,7 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil
case p.isStdPkg(pkgpath):
logger.Tracef(&config.EnableTrace_loader, "isStdPkg; pkgpath: %v", pkgpath)

pkgVFS = p.vfs.Std
filenames, datas, err = p.readDirFiles(p.vfs.Std, pkgpath, unitTestMode, extNames)
if err != nil {
logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
Expand All @@ -542,6 +545,7 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil

logger.Tracef(&config.EnableTrace_loader, "isSelfPkg; pkgpath=%v, relpkg=%v", pkgpath, relpkg)

pkgVFS = p.vfs.App
filenames, datas, err = p.readDirFiles(p.vfs.App, relpkg, unitTestMode, extNames)
if err != nil {
logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
Expand All @@ -553,6 +557,7 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil
default: // vendor
logger.Tracef(&config.EnableTrace_loader, "vendorPkg; pkgpath: %v", pkgpath)

pkgVFS = p.vfs.Vendor
filenames, datas, err = p.readDirFiles(p.vfs.Vendor, pkgpath, unitTestMode, extNames)
if err != nil {
logger.Tracef(&config.EnableTrace_loader, "err: %v", err)
Expand All @@ -579,6 +584,27 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil
files = append(files, f)
}

// read embed files
for _, f := range files {
for _, commentGroup := range f.Comments {
info := astutil.ParseCommentInfo(commentGroup)
if info.Embed == "" {
continue
}

if f.EmbedMap == nil {
f.EmbedMap = make(map[string]string)
}

data, err := fs.ReadFile(pkgVFS, info.Embed)
if err != nil {
continue
}

f.EmbedMap[info.Embed] = string(data)
}
}

return filenames, files, nil
}

Expand Down
38 changes: 37 additions & 1 deletion internal/loader/vfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,32 @@ import (
"strings"
"testing/fstest"

"wa-lang.org/wa/internal/ast/astutil"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/token"
"wa-lang.org/wa/waroot"
)

// 读取 embed 列表, 提前加载内嵌资源数据
func parseEmbedPathList(filename string, src interface{}) []string {
fset := token.NewFileSet()
f, err := parser.ParseFile(nil, fset, filename, src, parser.ParseComments)
if err != nil {
return nil
}

var ss []string
for _, doc := range f.Comments {
if info := astutil.ParseCommentInfo(doc); info.Embed != "" {
ss = append(ss, info.Embed)
}
}

return ss
}

// 根据路径加载需要的 vfs 和 manifest
func loadProgramFileMeta(cfg *config.Config, filename string, src interface{}) (
vfs *config.PkgVFS,
Expand Down Expand Up @@ -54,11 +75,26 @@ func loadProgramFileMeta(cfg *config.Config, filename string, src interface{}) (
// 构造入口文件
vfs = new(config.PkgVFS)
if vfs.App == nil {
vfs.App = fstest.MapFS{

mapFS := fstest.MapFS{
filepath.Base(filename): &fstest.MapFile{
Data: srcData,
},
}

// read embed list, and read file data
embedList := parseEmbedPathList(filename, string(srcData))
for _, name := range embedList {
path := filepath.Join(filepath.Dir(filename), name)
if data, err := os.ReadFile(path); err == nil {
mapFS[name] = &fstest.MapFile{
Data: data,
}

}
}

vfs.App = mapFS
}

if vfs.Std == nil {
Expand Down
1 change: 1 addition & 0 deletions internal/types/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {

check.collectObjects()

check.processGlobalEmbed()
check.processGenericFuncs()
check.processTypeOperators()

Expand Down
76 changes: 68 additions & 8 deletions internal/types/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,72 @@

package types

// #wa:embed path
// #wa:embed path limit
// #wa:embed path offset limit
// const s = "if-empty"
type embedInfo struct {
FilePath string
Offset int
Limit int
import (
"strconv"

"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/ast/astutil"
"wa-lang.org/wa/internal/token"
)

// 预处理全局变量文件嵌入
func (check *Checker) processGlobalEmbed() {
for obj, d := range check.objMap {
if varObj, ok := obj.(*Var); ok {
varSpec := varObj.Node().(*ast.ValueSpec)
varCommentInfo := astutil.ParseCommentInfo(varObj.NodeDoc())

if varCommentInfo.Embed == "" {
continue
}
if len(varSpec.Names) != 1 {
check.errorf(varObj.Pos(), "wa:embed only support one global")
return
}
if len(varSpec.Values) != 0 || varSpec.Type == nil {
check.errorf(varObj.Pos(), "wa:embed donot support init values")
return
}

typeIdent, _ := varSpec.Type.(*ast.Ident)
if typeIdent == nil {
check.errorf(varObj.Pos(), "wa:embed must have type")
return
}

scope, obj := check.pkg.scope.LookupParent(typeIdent.Name, varSpec.Pos())
if scope != Universe || obj.Type() != universeString {
check.errorf(varObj.Pos(), "wa:embed invalid global type %v", obj)
continue
}

// read file data
var embedFound bool
var embedData string
Loop:
for _, f := range check.files {
for k, v := range f.EmbedMap {
if k == varCommentInfo.Embed {
embedFound = true
embedData = v
break Loop
}
}
}
if !embedFound {
check.errorf(varObj.Pos(), "wa:embed file not found")
continue
}

varSpec.Values = []ast.Expr{
&ast.BasicLit{
ValuePos: varSpec.Pos(),
Kind: token.STRING,
Value: strconv.Quote(embedData),
},
}

d.init = varSpec.Values[0]
}
}
}
4 changes: 1 addition & 3 deletions internal/types/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,12 @@ func (obj *PkgName) Imported() *Package { return obj.imported }
type Const struct {
object
val constant.Value

embed *embedInfo
}

// NewConst returns a new constant with value val.
// The remaining arguments set the attributes found with all Objects.
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
return &Const{object{nil, nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos, nil}, val, nil}
return &Const{object{nil, nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos, nil}, val}
}

// Val returns the constant's value.
Expand Down
10 changes: 6 additions & 4 deletions internal/types/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ var Universe *Scope
var Unsafe *Package

var (
universeIota *Const
universeByte *Basic // uint8 alias, but has name "byte"
universeRune *Basic // int32 alias, but has name "rune"
universeAny Object
universeIota *Const
universeByte *Basic // uint8 alias, but has name "byte"
universeRune *Basic // int32 alias, but has name "rune"
universeString *Basic
universeAny Object
)

// Typ contains the predeclared *Basic types indexed by their
Expand Down Expand Up @@ -242,6 +243,7 @@ func init() {
universeIota = Universe.Lookup("iota").(*Const)
universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
universeString = Universe.Lookup("string").(*TypeName).typ.(*Basic)
universeAny = Universe.Lookup("any")
}

Expand Down

0 comments on commit 1a156e3

Please sign in to comment.