wa 命令代码重构优化

This commit is contained in:
chai2010
2023-07-26 20:06:56 +08:00
parent a65f4d1a6b
commit 7c1573fbf3
36 changed files with 266 additions and 194 deletions

View File

@@ -5,10 +5,9 @@ package appast
import (
"fmt"
"os"
"path/filepath"
"strings"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/token"
@@ -36,15 +35,18 @@ func CmdAstAction(c *cli.Context) error {
}
func PrintAST(filename string) error {
ext := strings.ToLower(filepath.Ext(filename))
if ext != ".wa" && ext != ".wz" {
if !appbase.HasExt(filename, ".wa", ".wz") {
return fmt.Errorf("%q is not Wa file", filename)
}
if fi, _ := os.Lstat(filename); fi == nil || fi.IsDir() {
if !appbase.PathExists(filename) {
return fmt.Errorf("%q not found", filename)
}
if !appbase.IsNativeFile(filename) {
return fmt.Errorf("%q must be file", filename)
}
fset := token.NewFileSet() // positions are relative to fset
fset := token.NewFileSet()
f, err := parser.ParseFile(nil, fset, filename, nil, 0)
if err != nil {
return err

View File

@@ -0,0 +1,50 @@
package appbase
import (
"fmt"
"strings"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/config"
)
// 输出路径
func MakeFlag_output() *cli.StringFlag {
return &cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "set output file",
Value: "",
}
}
// 构建的目标
func MakeFlag_target() *cli.StringFlag {
return &cli.StringFlag{
Name: "target",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
}
}
// 构建的 Tags
func MakeFlag_tags() *cli.StringFlag {
return &cli.StringFlag{
Name: "tags",
Usage: "set build tags",
}
}
func MakeFlag_ld_stack_size() *cli.IntFlag {
return &cli.IntFlag{
Name: "ld-stack-size",
Usage: "set stack size",
}
}
func MakeFlag_ld_max_memory() *cli.IntFlag {
return &cli.IntFlag{
Name: "ld-max-memory",
Usage: "set max memory size",
}
}

View File

@@ -0,0 +1,98 @@
// 版权 @2019 凹语言 作者。保留所有权利。
package appbase
import (
"os"
"strings"
"unicode"
)
// 路径名是否包含后缀, 忽略大小写区别
func HasExt(path string, exts ...string) bool {
if path == "" {
return false
}
if len(exts) == 0 {
return true
}
for _, ext := range exts {
if len(path) <= len(ext) {
continue
}
if strings.EqualFold(ext, path[len(path)-len(ext):]) {
return true
}
}
return false
}
// 本地路径存在
func PathExists(path string) bool {
_, err := os.Lstat(path)
return err == nil
}
// 是否为本地存在的文件, 并满足后缀名
func IsNativeFile(path string, exts ...string) bool {
if !HasExt(path, exts...) {
return false
}
fi, err := os.Lstat(path)
if err != nil {
return false
}
return !fi.IsDir()
}
// 是否为本地存在的目录
func IsNativeDir(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.IsDir()
}
// 替换后缀名
func ReplaceExt(path string, extOld, extNew string) string {
return path[:len(path)-len(extOld)] + extNew
}
// 为合法的 app 名字
func IsValidAppName(s string) bool {
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
return false
}
for _, c := range []rune(s) {
if c == '_' || (c >= '0' && c <= '9') || unicode.IsLetter(c) {
continue
}
return false
}
return true
}
// 为合法的包路径
func IsValidPkgpath(s string) bool {
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
return false
}
for _, c := range []rune(s) {
if c == '_' || c == '.' || c == '/' || (c >= '0' && c <= '9') {
continue
}
if unicode.IsLetter(c) {
continue
}
return false
}
var pkgname = s
if i := strings.LastIndex(s, "/"); i >= 0 {
pkgname = s[i+1:]
}
return IsValidAppName(pkgname)
}
// 是标准库路径

View File

@@ -0,0 +1,32 @@
// 版权 @2019 凹语言 作者。保留所有权利。
package appbase
import "testing"
func TestHasExt(t *testing.T) {
var tests = []struct {
path string
exts []string
ok bool
}{
{"", nil, false},
{"a", nil, true},
{"a.wa", []string{".wa"}, true},
{"a.wa", []string{".WA"}, true},
{"a.Wa", []string{".wa"}, true},
{"a.wa", []string{".wa", ".wz"}, true},
{".wa", []string{".wa"}, false},
{".wa", []string{}, true},
}
for i, tt := range tests {
expect := tt.ok
got := HasExt(tt.path, tt.exts...)
if expect != got {
t.Fatalf("%d: expect = %v, got = %v; // %v", i, expect, got, tt)
}
}
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
@@ -20,29 +19,11 @@ var CmdBuild = &cli.Command{
Name: "build",
Usage: "compile Wa source code",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "set output file",
Value: "",
},
&cli.StringFlag{
Name: "target",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.StringFlag{
Name: "tags",
Usage: "set build tags",
},
&cli.IntFlag{
Name: "ld-stack-size",
Usage: "set stack size",
},
&cli.IntFlag{
Name: "ld-max-memory",
Usage: "set max memory size",
},
appbase.MakeFlag_output(),
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
appbase.MakeFlag_ld_stack_size(),
appbase.MakeFlag_ld_max_memory(),
},
Action: CmdBuildAction,
}
@@ -65,22 +46,22 @@ func CmdBuildAction(c *cli.Context) error {
func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err error) {
// 路径是否存在
if _, err := os.Lstat(input); err != nil {
if !appbase.PathExists(input) {
fmt.Printf("%q not found\n", input)
os.Exit(1)
}
// 输出参数是否合法
if outfile != "" && hasExt(outfile, ".wasm") {
// 输出参数是否合法, 必须是 wasm
if outfile != "" && !appbase.HasExt(outfile, ".wasm") {
fmt.Printf("%q is not valid output path\n", outfile)
os.Exit(1)
}
// 只编译 wat 文件, 输出路径相同, 后缀名调整
if isFile(input) && hasExt(input, ".wat") {
if appbase.HasExt(input, ".wat") {
// 设置默认输出目标
if outfile == "" {
outfile = input[:len(input)-len(".wat")] + ".wasm"
outfile = appbase.ReplaceExt(input, ".wat", ".wasm")
}
watData, err := os.ReadFile(input)
@@ -106,7 +87,7 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
}
// 只编译 wa/wz 文件, 输出路径相同, 后缀名调整
if isFile(input) && hasExt(input, ".wa", ".wz") {
if appbase.HasExt(input, ".wa", ".wz") {
_, watOutput, err := buildWat(opt, input)
if err != nil {
fmt.Println(err)
@@ -115,11 +96,11 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
// 设置默认输出目标
if outfile == "" {
outfile = input[:len(input)-len(".wa")] + ".wasm"
outfile = appbase.ReplaceExt(input, ".wa", ".wasm")
}
// wat 写到文件
watOutfile := outfile[:len(outfile)-len(".wasm")] + ".wat"
watOutfile := appbase.ReplaceExt(outfile, ".wasm", ".wat")
err = os.WriteFile(watOutfile, watOutput, 0666)
if err != nil {
fmt.Printf("write %s failed: %v\n", outfile, err)
@@ -146,7 +127,7 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
// 构建目录
{
if !isDir(input) {
if !appbase.IsNativeDir(input) {
fmt.Printf("%q is not valid output path\n", outfile)
os.Exit(1)
}
@@ -157,8 +138,8 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
fmt.Printf("%q is invalid wa moudle\n", input)
os.Exit(1)
}
if !manifest.Valid() {
fmt.Printf("%q is invalid wa module\n", input)
if err := manifest.Valid(); err != nil {
fmt.Printf("%q is invalid wa module; %v\n", input, err)
os.Exit(1)
}
@@ -179,7 +160,7 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
}
// wat 写到文件
watOutfile := outfile[:len(outfile)-len(".wasm")] + ".wat"
watOutfile := appbase.ReplaceExt(outfile, ".wasm", ".wat")
err = os.WriteFile(watOutfile, watOutput, 0666)
if err != nil {
fmt.Printf("write %s failed: %v\n", outfile, err)
@@ -206,7 +187,6 @@ func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err
}
func buildWat(opt *appbase.Option, filename string) (*loader.Program, []byte, error) {
cfg := opt.Config()
prog, err := loader.LoadProgram(cfg, filename)
if err != nil {
@@ -221,37 +201,3 @@ func buildWat(opt *appbase.Option, filename string) (*loader.Program, []byte, er
return prog, []byte(output), nil
}
func hasExt(filename string, extList ...string) bool {
ext := strings.ToLower(filepath.Ext(filename))
return strInList(ext, extList)
}
func isDir(filename string) bool {
fi, err := os.Lstat(filename)
if err != nil {
return false
}
return fi.IsDir()
}
func isFile(filename string) bool {
fi, err := os.Lstat(filename)
if err != nil {
return false
}
if fi.IsDir() {
return false
}
return true
}
func strInList(s string, list []string) bool {
for _, x := range list {
if s == x {
return true
}
}
return false
}

View File

@@ -16,6 +16,11 @@ var CmdCir = &cli.Command{
Hidden: true,
Name: "cir",
Usage: "print cir code",
Flags: []cli.Flag{
appbase.MakeFlag_output(),
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
},
Action: func(c *cli.Context) error {
if c.NArg() == 0 {
fmt.Fprintf(os.Stderr, "no input file")

View File

@@ -10,7 +10,6 @@ import (
)
var CmdDev = &cli.Command{
// go run main.go debug
Hidden: true,
Name: "debug",
Usage: "only for dev/debug",

View File

@@ -10,6 +10,7 @@ import (
"strings"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
"wa-lang.org/wa/internal/format"
)
@@ -27,6 +28,11 @@ var CmdFmt = &cli.Command{
}
func Fmt(path string) error {
if appbase.IsNativeFile(path, ".wa", ".wz") {
_, err := fmtFile(path)
return err
}
if path == "" {
if _, err := os.Lstat("wa.mod.json"); err == nil {
path = "./..."

View File

@@ -7,12 +7,11 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"text/template"
"time"
"unicode"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
"wa-lang.org/wa/waroot"
)
@@ -53,11 +52,11 @@ func InitApp(name, pkgpath string, update bool) error {
if name == "" {
return fmt.Errorf("init failed: <%s> is empty", name)
}
if !isValidAppName(name) {
if !appbase.IsValidAppName(name) {
return fmt.Errorf("init failed: <%s> is invalid name", name)
}
if !isValidPkgpath(pkgpath) {
if !appbase.IsValidPkgpath(pkgpath) {
return fmt.Errorf("init failed: <%s> is invalid pkgpath", pkgpath)
}
@@ -157,37 +156,3 @@ func InitApp(name, pkgpath string, update bool) error {
return nil
}
func isValidAppName(s string) bool {
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
return false
}
for _, c := range []rune(s) {
if c == '_' || (c >= '0' && c <= '9') || unicode.IsLetter(c) {
continue
}
return false
}
return true
}
func isValidPkgpath(s string) bool {
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
return false
}
for _, c := range []rune(s) {
if c == '_' || c == '.' || c == '/' || (c >= '0' && c <= '9') {
continue
}
if unicode.IsLetter(c) {
continue
}
return false
}
var pkgname = s
if i := strings.LastIndex(s, "/"); i >= 0 {
pkgname = s[i+1:]
}
return isValidAppName(pkgname)
}

View File

@@ -3,13 +3,11 @@
package applex
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
"wa-lang.org/wa/internal/scanner"
"wa-lang.org/wa/internal/token"
)
@@ -34,7 +32,11 @@ var CmdLex = &cli.Command{
}
func Lex(filename string) error {
src, err := readSource(filename, nil)
if !appbase.IsNativeFile(filename, ".wa", ".wz") {
return fmt.Errorf("%q is not valid path", filename)
}
src, err := os.ReadFile(filename)
if err != nil {
return err
}
@@ -54,25 +56,3 @@ func Lex(filename string) error {
return nil
}
func readSource(filename string, src interface{}) ([]byte, error) {
if src != nil {
switch s := src.(type) {
case string:
return []byte(s), nil
case []byte:
return s, nil
case *bytes.Buffer:
if s != nil {
return s.Bytes(), nil
}
case io.Reader:
d, err := io.ReadAll(s)
return d, err
}
return nil, errors.New("invalid source")
}
d, err := os.ReadFile(filename)
return d, err
}

View File

@@ -21,21 +21,10 @@ var CmdNative = &cli.Command{
Name: "native",
Usage: "compile Wa source code to native executable",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "set output file",
Value: "",
},
&cli.StringFlag{
Name: "target",
Usage: "set native target",
Value: "",
},
&cli.StringFlag{
Name: "tags",
Usage: "set build tags",
},
appbase.MakeFlag_output(),
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
&cli.BoolFlag{
Name: "debug",
Usage: "dump orginal intermediate representation",

View File

@@ -5,12 +5,10 @@ package apprun
import (
"fmt"
"os"
"strings"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app/appbase"
"wa-lang.org/wa/internal/app/appbuild"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/wazero"
)
@@ -18,15 +16,8 @@ var CmdRun = &cli.Command{
Name: "run",
Usage: "compile and run Wa program",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "target",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.StringFlag{
Name: "tags",
Usage: "set build tags",
},
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
},
Action: CmdRunAction,
}

View File

@@ -17,6 +17,10 @@ var CmdSsa = &cli.Command{
Hidden: true,
Name: "ssa",
Usage: "print Wa ssa code",
Flags: []cli.Flag{
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
},
Action: func(c *cli.Context) error {
if c.NArg() == 0 {
fmt.Fprintf(os.Stderr, "no input file")

View File

@@ -23,15 +23,8 @@ var CmdTest = &cli.Command{
Name: "test",
Usage: "test Wa packages",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "target",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.StringFlag{
Name: "tags",
Usage: "set build tags",
},
appbase.MakeFlag_target(),
appbase.MakeFlag_tags(),
},
Action: func(c *cli.Context) error {
if c.NArg() < 1 {

View File

@@ -70,7 +70,7 @@ func Main() {
// 没有参数时显示 help 信息
cliApp.Action = func(c *cli.Context) error {
if c.NArg() > 0 {
fmt.Println("unknown args:", strings.Join(c.Args().Slice(), " "))
fmt.Println("unknown command:", strings.Join(c.Args().Slice(), " "))
os.Exit(1)
}
cli.ShowAppHelpAndExit(c, 0)

View File

@@ -110,22 +110,22 @@ func LoadManifest(vfs fs.FS, appPath string) (p *Manifest, err error) {
return p, nil
}
func (p *Manifest) Valid() bool {
if p.Root == "" || p.MainPkg == "" {
return false
func (p *Manifest) Valid() error {
if p.Root == "" {
return fmt.Errorf("root is empty")
}
if p.Pkg.Name == "" || p.Pkg.Pkgpath == "" {
return false
if p.MainPkg == "" {
return fmt.Errorf("main package is empty")
}
if !isValidAppName(p.Pkg.Name) {
return false
return fmt.Errorf("%q is invalid app name", p.Pkg.Name)
}
if !isValidPkgpath(p.Pkg.Pkgpath) {
return false
return fmt.Errorf("%q is invalid pkg path", p.Pkg.Pkgpath)
}
return true
return nil
}
func (p *Manifest) JSONString() string {

1
waroot/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
hello.wat

1
waroot/examples/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.wat

1
waroot/examples/brainfuck/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/brainfuck",
"name": "brainfuck",
"pkgpath": "brainfuck",
"version": "0.0.1",
"authors": ["author", "author2"],

1
waroot/examples/hello/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/hello",
"name": "hello",
"pkgpath": "myapp",
"version": "0.0.1",
"authors": ["author", "author2"],

1
waroot/examples/misc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.wat

1
waroot/examples/pkg/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/hello",
"name": "hello",
"pkgpath": "pkg",
"version": "0.0.1",
"authors": ["author", "author2"],

1
waroot/examples/prime/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

1
waroot/examples/reftoptr/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/reftoptr",
"name": "reftoptr",
"pkgpath": "reftoptr",
"version": "0.0.1",
"authors": ["author", "author2"],

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/snake",
"name": "snake",
"pkgpath": "snake",
"version": "0.0.1",
"authors": ["author", "author2"],

View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/wz/烧脑虚拟机",
"name": "烧脑虚拟机",
"pkgpath": "烧脑虚拟机",
"version": "0.0.1",
"authors": ["author", "author2"],

View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/wz/hello",
"name": "hello",
"pkgpath": "hello",
"version": "0.0.1",
"authors": ["author", "author2"],

1
waroot/examples/wz/素数/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output

View File

@@ -1,5 +1,5 @@
{
"name": "_examples/wz/素数",
"name": "素数",
"pkgpath": "素数",
"version": "0.0.1",
"authors": ["author", "author2"],

1
waroot/misc/_example_app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/output