...
 
Commits (18)
    https://gitcode.net/wa-lang/wa/-/commit/783d26fc35f05422fceec4779515a90d1ef39be6 本地 play 支持凹中文 2023-06-25T20:13:58+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/be17597bdba7f0655f913ac3158e7f2cce4a5dd9 添加手册-2.1 2023-06-25T21:29:43+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/61571ac7b059bfb44f25f550d9be20434dc7fbf7 Merge branch 'master' of https://gitee.com/wa-lang/wa 2023-06-25T21:29:57+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/8201374e1700dbcded9abb2ba43f29c244182979 zz 2023-06-26T11:03:11+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/0a31dc803b74cf81d746fc2592dac562702593f8 增加代码文件类型识别语法 2023-06-26T22:40:51+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/52f3c77d3707559dc8c446cc304536c1f494281f 改进 wz 语法支持 2023-06-26T23:32:52+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/bcadb4704879f4f3fe2aa6026db454dd142ddccb 删除 playground 中输出的调试信息 2023-06-26T23:45:02+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/db9d81ad4df3f59404a0a357187ee77d35d19d3a 完善 wat 导出的中文名字处理 2023-06-27T00:58:24+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/a61c9d839c4bbae362872d97e4eba3a85da43923 修复并完善 fmt 命令 2023-06-27T07:22:06+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/6cf3f999e09fd487ef488e8fa6e8fdcc861861fd 增加mvp os 2023-06-27T21:40:40+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/14d79a0b71979b6897c0d27b97d050b74f4b718b Merge branch 'master' of https://gitee.com/wa-lang/wa 2023-06-27T21:44:58+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/08da0802698f8e1f97a50796ccd19a96ceb9146f 完善 MVP 目标定义 2023-06-27T23:20:25+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/9b9870b2f94ad498989b754eae2a00e9218e359d 完善 run 命令对相对路径文件的支持 2023-06-28T01:12:44+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/edaa2dd38098a58760eb2cfa655d04ae9bebdc87 完善 syntax 语法格式 2023-06-28T01:17:26+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/86a784e0198d96284bf806580db92845bb6eb791 fmt 支持 global 关键字 2023-06-28T02:07:11+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/0a5a671f5da3d7829d8ff7d2a37111beb86bc900 CI 添加 fmt 并包含更多测试 2023-06-28T19:28:16+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/dfa6b44319be468122f54020aea4e2cf32f36884 屏蔽 parser 失败的测试 2023-06-28T19:32:25+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/b0839d7a2c0df4b52d4d0e937e54d6cb1205493b 增加 版本号 文件 2023-06-28T22:20:25+08:00 chai2010 chaishushan@gmail.com
......@@ -38,6 +38,7 @@ ci-test-all:
go run main.go test unicode/utf8
@echo "== std ok =="
go run main.go ./waroot/hello.wa
cd waroot && go run ../main.go hello.wa
make -C ./waroot/examples ci-test-all
......
......@@ -11,6 +11,7 @@ import (
"wa-lang.org/wa/internal/format"
"wa-lang.org/wa/internal/loader"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wa/internal/wamime"
)
// 调试参数
......@@ -102,3 +103,8 @@ func FormatCode(filename, code string) (string, error) {
}
return string(data), nil
}
// 获取代码语法类型
func GetCodeSyntax(filename string, code []byte) string {
return wamime.GetCodeMime(filename, code)
}
......@@ -59,3 +59,28 @@ func ExampleRunCode_args() {
// 0 : aa
// 1 : bb
}
func ExampleRunCode_wz() {
const code = `
#wa:syntax=wz
引于 "书"
【启】:
书·说:"你好,凹语言中文版!"
`
output, err := api.RunCode(api.DefaultConfig(), "hello.wa", code)
if err != nil {
if len(output) != 0 {
log.Println(string(output))
}
log.Fatal(err)
}
fmt.Print(string(output))
// Output:
// 你好,凹语言中文版!
}
......@@ -8,7 +8,7 @@ import (
"wa-lang.org/wa/api"
)
func _ExampleFormatCode() {
func ExampleFormatCode() {
s, err := api.FormatCode("hello.wa", "func add(a:i32, b:i32)=>i32 {return a+b}")
if err != nil {
panic(err)
......
......@@ -14,19 +14,32 @@ import (
func Fmt(path string) error {
if path == "" {
path, _ = os.Getwd()
path = "."
}
var waFileList []string
if strings.HasSuffix(path, "...") {
waFileList = getDirWaFileList(strings.TrimSuffix(path, "..."))
switch {
case strings.HasSuffix(path, ".wa"):
waFileList = append(waFileList, path)
case strings.HasSuffix(path, ".wz"):
waFileList = append(waFileList, path)
case strings.HasSuffix(path, "..."):
waFileList = getDirWaFileList(
strings.TrimSuffix(path, "..."),
true, ".wa", ".wz", // 包含子目录
)
default:
// 不包含子目录
waFileList = getDirWaFileList(
path, false, ".wa", ".wz",
)
}
var changedFileList []string
for _, s := range waFileList {
changed, err := fmtFile(s)
if err != nil {
return err
return fmt.Errorf("%s: %w", s, err)
}
if changed {
changedFileList = append(changedFileList, s)
......@@ -51,8 +64,28 @@ func fmtFile(path string) (changed bool, err error) {
return true, nil
}
func getDirWaFileList(dir string) []string {
func getDirWaFileList(dir string, walkSubDir bool, extList ...string) []string {
var waFileList []string
if !walkSubDir {
files, err := os.ReadDir(".")
if err != nil {
return nil
}
for _, file := range files {
if file.IsDir() {
continue
}
for _, ext := range extList {
if strings.HasSuffix(file.Name(), ext) {
waFileList = append(waFileList, filepath.Join(dir, file.Name()))
}
}
}
sort.Strings(waFileList)
return waFileList
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
......@@ -60,8 +93,11 @@ func getDirWaFileList(dir string) []string {
if info.IsDir() {
return nil
}
if strings.HasSuffix(path, ".wa") {
waFileList = append(waFileList, path)
for _, ext := range extList {
if strings.HasSuffix(path, ext) {
waFileList = append(waFileList, path)
return nil
}
}
return nil
})
......
......@@ -8,6 +8,7 @@ import (
"net/http"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/wamime"
)
func (p *WebServer) fmtHandler(w http.ResponseWriter, r *http.Request) {
......@@ -28,7 +29,12 @@ func (p *WebServer) fmtHandler(w http.ResponseWriter, r *http.Request) {
}
func (p *WebServer) fmtCode(code []byte) (*fmtResponse, error) {
output, err := api.FormatCode("prog.wa", string(code))
filename := "prog.wa"
if wamime.GetCodeMime(filename, code) == "wz" {
filename = "prog.wz"
}
output, err := api.FormatCode(filename, string(code))
if err != nil {
resp := &fmtResponse{
Error: err.Error(),
......
......@@ -10,6 +10,7 @@ import (
"os"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/wamime"
)
func (p *WebServer) runHandler(w http.ResponseWriter, r *http.Request) {
......@@ -41,7 +42,12 @@ func (p *WebServer) compileAndRun(req *Request) (*Response, error) {
}
defer os.RemoveAll(tmpDir)
result, err := api.RunCode(api.DefaultConfig(), "prog.wa", req.Body)
filename := "prog.wa"
if wamime.GetCodeMime(filename, []byte(req.Body)) == "wz" {
filename = "prog.wz"
}
result, err := api.RunCode(api.DefaultConfig(), filename, req.Body)
if err != nil {
resp := &Response{Errors: err.Error()}
return resp, nil
......
......@@ -9,9 +9,7 @@ package app
import (
"fmt"
"os"
"runtime/debug"
"strings"
"time"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/3rdparty/cli"
......@@ -20,25 +18,19 @@ import (
"wa-lang.org/wa/internal/lsp"
"wa-lang.org/wa/internal/wabt"
"wa-lang.org/wa/internal/wazero"
"wa-lang.org/wa/waroot"
)
func Main() {
cliApp := cli.NewApp()
cliApp.Name = "Wa"
cliApp.Usage = "Wa is a tool for managing Wa source code."
cliApp.Version = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
if info.Main.Version != "" {
return info.Main.Version
}
}
return "devel:" + time.Now().Format("2006-01-02+15:04:05")
}()
cliApp.Version = waroot.GetVersion()
cliApp.Flags = []cli.Flag{
&cli.StringFlag{
Name: "target",
Usage: "set target os (arduino|chrome|wasi)",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.BoolFlag{
......@@ -54,10 +46,7 @@ func Main() {
}
cliApp.Before = func(c *cli.Context) error {
switch c.String("target") {
case "wasi", "arduino", "chrome":
// OK
default:
if !config.CheckWaOS(c.String("target")) {
fmt.Printf("unknown target: %s\n", c.String("target"))
os.Exit(1)
}
......@@ -141,7 +130,7 @@ func Main() {
Flags: []cli.Flag{
&cli.StringFlag{
Name: "target",
Usage: "set target os (wasi|arduino|chrome)",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.StringFlag{
......@@ -166,7 +155,7 @@ func Main() {
},
&cli.StringFlag{
Name: "target",
Usage: "set target os (wasi|arduino|chrome)",
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
Value: config.WaOS_Default,
},
&cli.StringFlag{
......
......@@ -3,8 +3,6 @@
package main
import (
"fmt"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/backends/compiler_wat"
"wa-lang.org/wa/internal/config"
......@@ -65,8 +63,6 @@ func waBuildFile(cfg *config.Config, filename string, src interface{}) (wat []by
return nil, err
}
fmt.Println(prog.DebugString())
watOut, err := compiler_wat.New().Compile(prog, "main")
return []byte(watOut), err
}
......@@ -2143,7 +2143,7 @@ func output() {
if !lflag {
fmt.Fprintf(ftable, "\n//line yacctab:1")
}
fmt.Fprintf(ftable, "\nvar %sExca = [...]int{\n", prefix)
fmt.Fprintf(ftable, "\nglobal %sExca = [...]int{\n", prefix)
if len(errors) > 0 {
stateTable = make([]Row, nstate)
......
......@@ -868,7 +868,7 @@ type (
GenDecl struct {
Doc *CommentGroup // associated documentation; or nil
TokPos token.Pos // position of Tok
Tok token.Token // IMPORT, CONST, TYPE, VAR
Tok token.Token // IMPORT, CONST, TYPE, VAR, GLOBAL
Lparen token.Pos // position of '(', if any
Specs []Spec
Rparen token.Pos // position of ')', if any
......
......@@ -536,7 +536,7 @@ func NodeDescription(n ast.Node) string {
return "constant declaration"
case token.TYPE:
return "type declaration"
case token.VAR:
case token.VAR, token.GLOBAL:
return "variable declaration"
}
case *ast.Ident:
......
......@@ -184,7 +184,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
switch rt := recv.Type().(type) {
case *types.Named:
internal += GenSymbolName(rt.Obj().Name())
external += rt.Obj().Name()
external += GenSymbolName(rt.Obj().Name())
case *types.Pointer:
btype, ok := rt.Elem().(*types.Named)
......@@ -192,7 +192,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
panic("Unreachable")
}
internal += GenSymbolName(btype.Obj().Name())
external += btype.Obj().Name()
external += GenSymbolName(btype.Obj().Name())
default:
panic("Unreachable")
......@@ -205,7 +205,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
internal += "."
external += "."
internal += GenSymbolName(f.Name())
external += f.Name()
external += GenSymbolName(f.Name())
case *types.Func:
internal, external = GetPkgMangleName(f.Pkg().Path())
......@@ -216,7 +216,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
switch rt := recv.Type().(type) {
case *types.Named:
internal += GenSymbolName(rt.Obj().Name())
external += rt.Obj().Name()
external += GenSymbolName(rt.Obj().Name())
case *types.Pointer:
btype, ok := rt.Elem().(*types.Named)
......@@ -224,7 +224,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
panic("Unreachable")
}
internal += GenSymbolName(btype.Obj().Name())
external += btype.Obj().Name()
external += GenSymbolName(btype.Obj().Name())
default:
panic("Unreachable")
......@@ -233,7 +233,7 @@ func GetFnMangleName(v interface{}) (internal string, external string) {
internal += "."
external += "."
internal += GenSymbolName(f.Name())
external += f.Name()
external += GenSymbolName(f.Name())
}
return internal, external
......@@ -251,7 +251,7 @@ func GetPkgMangleName(pkg_path string) (string, string) {
symbol_name += GenSymbolName(p)
symbol_name += "$"
}
exp_name += pkg_path
exp_name += GenSymbolName(pkg_path)
symbol_name += GenSymbolName(pkg_path)
return symbol_name, exp_name
}
......
......@@ -36,6 +36,7 @@ const (
WaOS_arduino = "arduino" // Arduino 平台
WaOS_chrome = "chrome" // Chrome 浏览器
WaOS_wasi = "wasi" // WASI 接口
WaOS_mvp = "mvp" // MVP 接口, 最小可用
)
// 体系结构类型
......@@ -61,6 +62,7 @@ var WaOS_List = []string{
WaOS_arduino,
WaOS_chrome,
WaOS_wasi,
WaOS_mvp,
}
// CPU 列表
......@@ -71,3 +73,13 @@ var WaArch_List = []string{
WaArch_riscv64,
WaArch_wasm,
}
// 检查 OS 值是否 OK
func CheckWaOS(os string) bool {
for _, x := range WaOS_List {
if x == os {
return true
}
}
return false
}
......@@ -27,6 +27,7 @@ import (
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/printer"
"wa-lang.org/wa/internal/token"
"wa-lang.org/wa/internal/wamime"
)
var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
......@@ -65,11 +66,15 @@ func File(vfs fs.FS, filename string, src interface{}) (text []byte, changed boo
if err != nil {
return nil, false, err
}
golden, err := SourceFile(text)
if !bytes.Equal(text, golden) {
return text, false, nil
// TODO: 支持中文格式化
if wamime.GetCodeMime(filename, text) == "wa" {
golden, err := SourceFile(text)
if bytes.Equal(text, golden) {
return text, false, nil
}
return golden, true, err
}
return golden, true, nil
return text, false, nil
}
// Node formats node in canonical gofmt style and writes the result to dst.
......
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package format
import (
"bytes"
"io/ioutil"
"strings"
"testing"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/token"
)
const testfile = "format_test.go.wa"
func diff(t *testing.T, dst, src []byte) {
line := 1
offs := 0 // line offset
for i := 0; i < len(dst) && i < len(src); i++ {
d := dst[i]
s := src[i]
if d != s {
t.Errorf("dst:%d: %s\n", line, dst[offs:i+1])
t.Errorf("src:%d: %s\n", line, src[offs:i+1])
return
}
if s == '\n' {
line++
offs = i + 1
}
}
if len(dst) != len(src) {
t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src)
}
}
func TestNode(t *testing.T) {
src, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
fset := token.NewFileSet()
file, err := parser.ParseFile(nil, fset, testfile, src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
if err = Node(&buf, fset, file); err != nil {
t.Fatal("Node failed:", err)
}
diff(t, buf.Bytes(), src)
}
func TestSource(t *testing.T) {
src, err := ioutil.ReadFile(testfile)
if err != nil {
t.Fatal(err)
}
res, err := Source(src)
if err != nil {
t.Fatal("Source failed:", err)
}
diff(t, res, src)
}
// Test cases that are expected to fail are marked by the prefix "ERROR".
// The formatted result must look the same as the input for successful tests.
var tests = []string{
// declaration lists
`import "wa-lang.org/wa/internal/format"`,
"var x int",
"var x int\n\ntype T struct{}",
// statement lists
"x := 0",
"f(a, b, c)\nvar x int = f(1, 2, 3)",
// indentation, leading and trailing space
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation added inside raw strings
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\n\t\tfoo\n`\n\n\n", // no indentation removed inside raw strings
// comments
"/* Comment */",
"\t/* Comment */ ",
"\n/* Comment */ ",
"i := 5 /* Comment */", // issue #5551
"\ta()\n//line :1", // issue #11276
"\t//xxx\n\ta()\n//line :2", // issue #11276
"\ta() //line :1\n\tb()\n", // issue #11276
"x := 0\n//line :1\n//line :2", // issue #11276
// whitespace
"", // issue #11275
" ", // issue #11275
"\t", // issue #11275
"\t\t", // issue #11275
"\n", // issue #11275
"\n\n", // issue #11275
"\t\n", // issue #11275
// erroneous programs
"ERROR1 + 2 +",
"ERRORx := 0",
}
func String(s string) (string, error) {
res, err := Source([]byte(s))
if err != nil {
return "", err
}
return string(res), nil
}
func TestPartial(t *testing.T) {
for _, src := range tests {
if strings.HasPrefix(src, "ERROR") {
// test expected to fail
src = src[5:] // remove ERROR prefix
res, err := String(src)
if err == nil && res == src {
t.Errorf("formatting succeeded but was expected to fail:\n%q", src)
}
} else {
// test expected to succeed
res, err := String(src)
if err != nil {
t.Errorf("formatting failed (%s):\n%q", err, src)
} else if res != src {
t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res)
}
}
}
}
......@@ -20,6 +20,7 @@ import (
"wa-lang.org/wa/internal/ssa"
"wa-lang.org/wa/internal/token"
"wa-lang.org/wa/internal/types"
"wa-lang.org/wa/internal/wamime"
"wa-lang.org/wa/waroot"
)
......@@ -462,7 +463,7 @@ func (p *_Loader) ParseDir(pkgpath string) (filenames []string, files []*ast.Fil
for i, filename := range filenames {
var f *ast.File
if p.hasExt(filename, ".wz") {
if wamime.GetCodeMime(filename, datas[i]) == "wz" {
f, err = wzparser.ParseFile(nil, p.prog.Fset, filename, datas[i], wzparser.AllErrors|wzparser.ParseComments)
} else {
f, err = parser.ParseFile(nil, p.prog.Fset, filename, datas[i], parser.AllErrors|parser.ParseComments)
......
......@@ -43,7 +43,7 @@ func loadProgramFileMeta(cfg *config.Config, filename string, src interface{}) (
Root: "__main__",
MainPkg: "__main__",
Pkg: config.Manifest_package{
Name: filename,
Name: filepath.Base(filename),
Pkgpath: "__main__",
},
}
......@@ -55,7 +55,7 @@ func loadProgramFileMeta(cfg *config.Config, filename string, src interface{}) (
vfs = new(config.PkgVFS)
if vfs.App == nil {
vfs.App = fstest.MapFS{
filename: &fstest.MapFile{
filepath.Base(filename): &fstest.MapFile{
Data: srcData,
},
}
......
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements a parser test harness. The files in the testdata
// directory are parsed and the errors reported are compared against the
// error messages expected in the test files. The test files must end in
// .src rather than .go so that they are not disturbed by gofmt runs.
//
// Expected errors are indicated in the test files by putting a comment
// of the form /* ERROR "rx" */ immediately following an offending token.
// The harness will verify that an error matching the regular expression
// rx is reported at that source position.
//
// For instance, the following test file indicates that a "not declared"
// error should be reported for the undeclared variable x:
//
// package p
// func f() {
// _ = x /* ERROR "not declared" */ + 1
// }
package parser
import (
"io/fs"
"io/ioutil"
"path/filepath"
"regexp"
"strings"
"testing"
"wa-lang.org/wa/internal/scanner"
"wa-lang.org/wa/internal/token"
)
const testdata = "testdata"
// getFile assumes that each filename occurs at most once
func getFile(fset *token.FileSet, filename string) (file *token.File) {
fset.Iterate(func(f *token.File) bool {
if f.Name() == filename {
if file != nil {
panic(filename + " used multiple times")
}
file = f
}
return true
})
return file
}
func getPos(fset *token.FileSet, filename string, offset int) token.Pos {
if f := getFile(fset, filename); f != nil {
return f.Pos(offset)
}
return token.NoPos
}
// ERROR comments must be of the form /* ERROR "rx" */ and rx is
// a regular expression that matches the expected error message.
// The special form /* ERROR HERE "rx" */ must be used for error
// messages that appear immediately after a token, rather than at
// a token's position.
//
var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`)
// expectedErrors collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token.Pos]string {
errors := make(map[token.Pos]string)
var s scanner.Scanner
// file was parsed already - do not add it again to the file
// set otherwise the position information returned here will
// not match the position information collected by the parser
s.Init(getFile(fset, filename), src, nil, scanner.ScanComments)
var prev token.Pos // position of last non-comment, non-semicolon token
var here token.Pos // position immediately after the token at position prev
for {
pos, tok, lit := s.Scan()
switch tok {
case token.EOF:
return errors
case token.COMMENT:
s := errRx.FindStringSubmatch(lit)
if len(s) == 3 {
pos := prev
if s[1] == "HERE" {
pos = here
}
errors[pos] = string(s[2])
}
case token.SEMICOLON:
// don't use the position of auto-inserted (invisible) semicolons
if lit != ";" {
break
}
fallthrough
default:
prev = pos
var l int // token length
if tok.IsLiteral() {
l = len(lit)
} else {
l = len(tok.String())
}
here = prev + token.Pos(l)
}
}
}
// compareErrors compares the map of expected error messages with the list
// of found errors and reports discrepancies.
//
func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) {
for _, error := range found {
// error.Pos is a token.Position, but we want
// a token.Pos so we can do a map lookup
pos := getPos(fset, error.Pos.Filename, error.Pos.Offset)
if msg, found := expected[pos]; found {
// we expect a message at pos; check if it matches
rx, err := regexp.Compile(msg)
if err != nil {
t.Errorf("%s: %v", error.Pos, err)
continue
}
if match := rx.MatchString(error.Msg); !match {
t.Errorf("%s: %q does not match %q", error.Pos, error.Msg, msg)
continue
}
// we have a match - eliminate this error
delete(expected, pos)
} else {
// To keep in mind when analyzing failed test output:
// If the same error position occurs multiple times in errors,
// this message will be triggered (because the first error at
// the position removes this position from the expected errors).
t.Errorf("%s: unexpected error: %s", error.Pos, error.Msg)
}
}
// there should be no expected errors left
if len(expected) > 0 {
t.Errorf("%d errors not reported:", len(expected))
for pos, msg := range expected {
t.Errorf("%s: %s\n", fset.Position(pos), msg)
}
}
}
func checkErrors(vfs fs.FS, t *testing.T, filename string, input interface{}) {
src, err := readSource(vfs, filename, input)
if err != nil {
t.Error(err)
return
}
fset := token.NewFileSet()
_, err = ParseFile(vfs, fset, filename, src, DeclarationErrors|AllErrors)
found, ok := err.(scanner.ErrorList)
if err != nil && !ok {
t.Error(err)
return
}
found.RemoveMultiples()
// we are expecting the following errors
// (collect these after parsing a file so that it is found in the file set)
expected := expectedErrors(fset, filename, src)
// verify errors returned by the parser
compareErrors(t, fset, expected, found)
}
func TestErrors(t *testing.T) {
list, err := ioutil.ReadDir(testdata)
if err != nil {
t.Fatal(err)
}
for _, fi := range list {
name := fi.Name()
if !fi.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".src") {
checkErrors(nil, t, filepath.Join(testdata, name), nil)
}
}
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the exported entry points for invoking the parser.
package parser
import (
"bytes"
"errors"
"io"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"strings"
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/token"
)
// If src != nil, readSource converts src to a []byte if possible;
// otherwise it returns an error. If src == nil, readSource returns
// the result of reading the file specified by filename.
//
func readSource(vfs fs.FS, 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:
// is io.Reader, but src is already available in []byte form
if s != nil {
return s.Bytes(), nil
}
case io.Reader:
return ioutil.ReadAll(s)
}
return nil, errors.New("invalid source")
}
if vfs != nil {
return fs.ReadFile(vfs, filename)
}
return os.ReadFile(filename)
}
// A Mode value is a set of flags (or 0).
// They control the amount of source code parsed and other optional
// parser functionality.
//
type Mode uint
const (
PackageClauseOnly Mode = 1 << iota // stop parsing after package clause
ImportsOnly // stop parsing after import declarations
ParseComments // parse comments and add them to AST
Trace // print a trace of parsed productions
DeclarationErrors // report declaration errors
SpuriousErrors // same as AllErrors, for backward-compatibility
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
)
// ParseFile parses the source code of a single Go source file and returns
// the corresponding ast.File node. The source code may be provided via
// the filename of the source file, or via the src parameter.
//
// If src != nil, ParseFile parses the source from src and the filename is
// only used when recording position information. The type of the argument
// for the src parameter must be string, []byte, or io.Reader.
// If src == nil, ParseFile parses the file specified by filename.
//
// The mode parameter controls the amount of source text parsed and other
// optional parser functionality. Position information is recorded in the
// file set fset, which must not be nil.
//
// If the source couldn't be read, the returned AST is nil and the error
// indicates the specific failure. If the source was read but syntax
// errors were found, the result is a partial AST (with ast.Bad* nodes
// representing the fragments of erroneous source code). Multiple errors
// are returned via a scanner.ErrorList which is sorted by file position.
//
func ParseFile(vfs fs.FS, fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
if fset == nil {
panic("parser.ParseFile: no token.FileSet provided (fset == nil)")
}
// get source
text, err := readSource(vfs, filename, src)
if err != nil {
return nil, err
}
var p parser
defer func() {
if e := recover(); e != nil {
// resume same panic if it's not a bailout
if _, ok := e.(bailout); !ok {
panic(e)
}
}
// set result values
if f == nil {
// source is not a valid Go source file - satisfy
// ParseFile API and return a valid (but) empty
// *ast.File
f = &ast.File{
Name: new(ast.Ident),
Scope: ast.NewScope(nil),
}
}
p.errors.Sort()
err = p.errors.Err()
}()
// parse source
p.init(fset, filename, text, mode)
f = p.parseFile()
return
}
// ParseDir calls ParseFile for all files with names ending in ".wa" in the
// directory specified by path and returns a map of package name -> package
// AST with all the packages found.
//
// If filter != nil, only the files with os.FileInfo entries passing through
// the filter (and ending in ".wa") are considered. The mode bits are passed
// to ParseFile unchanged. Position information is recorded in fset, which
// must not be nil.
//
// If the directory couldn't be read, a nil map and the respective error are
// returned. If a parse error occurred, a non-nil but incomplete map and the
// first error encountered are returned.
//
func ParseDir(vfs fs.FS, fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
fd, err := os.Open(path)
if err != nil {
return nil, err
}
defer fd.Close()
list, err := fd.Readdir(-1)
if err != nil {
return nil, err
}
pkgs = make(map[string]*ast.Package)
for _, d := range list {
if strHasSuffix(d.Name(), ".wa") {
if filter == nil || filter(d) {
filename := filepath.Join(path, d.Name())
if src, err := ParseFile(vfs, fset, filename, nil, mode); err == nil {
name := src.Name.Name
pkg, found := pkgs[name]
if !found {
pkg = &ast.Package{
Name: name,
Files: make(map[string]*ast.File),
}
pkgs[name] = pkg
}
pkg.Files[filename] = src
} else if first == nil {
first = err
}
}
}
}
return
}
func strHasSuffix(s string, ext ...string) bool {
for _, suffix := range ext {
if strings.HasSuffix(s, suffix) {
return true
}
}
return false
}
// ParseExprFrom is a convenience function for parsing an expression.
// The arguments have the same meaning as for ParseFile, but the source must
// be a valid Go (type or value) expression. Specifically, fset must not
// be nil.
//
func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (ast.Expr, error) {
if fset == nil {
panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)")
}
// get source
text, err := readSource(nil, filename, src)
if err != nil {
return nil, err
}
var p parser
defer func() {
if e := recover(); e != nil {
// resume same panic if it's not a bailout
if _, ok := e.(bailout); !ok {
panic(e)
}
}
p.errors.Sort()
err = p.errors.Err()
}()
// parse expr
p.init(fset, filename, text, mode)
// Set up pkg-level scopes to avoid nil-pointer errors.
// This is not needed for a correct expression x as the
// parser will be ok with a nil topScope, but be cautious
// in case of an erroneous x.
p.openScope()
p.pkgScope = p.topScope
e := p.parseRhsOrType()
p.closeScope()
assert(p.topScope == nil, "unbalanced scopes")
// If a semicolon was inserted, consume it;
// report an error if there's more tokens.
if p.tok == token.SEMICOLON && p.lit == "\n" {
p.next()
}
p.expect(token.EOF)
if p.errors.Len() > 0 {
p.errors.Sort()
return nil, p.errors.Err()
}
return e, nil
}
// ParseExpr is a convenience function for obtaining the AST of an expression x.
// The position information recorded in the AST is undefined. The filename used
// in error messages is the empty string.
//
func ParseExpr(x string) (ast.Expr, error) {
return ParseExprFrom(token.NewFileSet(), "", []byte(x), 0)
}
......@@ -2394,7 +2394,7 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
p.expectSemi() // call before accessing p.linecomment
switch keyword {
case token.VAR:
case token.VAR, token.GLOBAL:
if typ == nil && values == nil {
p.error(pos, "missing variable type or initialization")
}
......@@ -2417,7 +2417,7 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
Comment: p.lineComment,
}
kind := ast.Con
if keyword == token.VAR {
if keyword == token.VAR || keyword == token.GLOBAL {
kind = ast.Var
}
p.declare(spec, iota, p.topScope, kind, idents...)
......@@ -2460,10 +2460,6 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
doc := p.leadComment
pos := p.expect(keyword)
if keyword == token.GLOBAL {
keyword = token.VAR // TODO(chai2010): AST 支持 global
}
var lparen, rparen token.Pos
var list []ast.Spec
if p.tok == token.LPAREN {
......
此差异已折叠。
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parser
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/token"
)
var validFiles = []string{
"parser.go.wa",
"parser_test.go.wa",
"error_test.go.wa",
"short_test.go.wa",
}
func TestParse(t *testing.T) {
for _, filename := range validFiles {
_, err := ParseFile(nil, token.NewFileSet(), filename, nil, DeclarationErrors)
if err != nil {
t.Fatalf("ParseFile(%s): %v", filename, err)
}
}
}
func nameFilter(filename string) bool {
switch filename {
case "parser.go", "interface.go", "parser_test.go":
return true
case "parser.go.orig":
return true // permit but should be ignored by ParseDir
}
return false
}
func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
func TestParseDir(t *testing.T) {
path := "."
pkgs, err := ParseDir(nil, token.NewFileSet(), path, dirFilter, 0)
if err != nil {
t.Fatalf("ParseDir(%s): %v", path, err)
}
if n := len(pkgs); n != 1 {
t.Errorf("got %d packages; want 1", n)
}
pkg := pkgs["parser"]
if pkg == nil {
t.Errorf(`package "parser" not found`)
return
}
if n := len(pkg.Files); n != 3 {
t.Errorf("got %d package files; want 3", n)
}
for filename := range pkg.Files {
if !nameFilter(filename) {
t.Errorf("unexpected package file: %s", filename)
}
}
}
func TestParseExpr(t *testing.T) {
// just kicking the tires:
// a valid arithmetic expression
src := "a + b"
x, err := ParseExpr(src)
if err != nil {
t.Errorf("ParseExpr(%q): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.BinaryExpr); !ok {
t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
}
// a valid type expression
src = "struct{x *int}"
x, err = ParseExpr(src)
if err != nil {
t.Errorf("ParseExpr(%q): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.StructType); !ok {
t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x)
}
// an invalid expression
src = "a + *"
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
// a valid expression followed by extra tokens is invalid
src = "a[i] := x"
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
// a semicolon is not permitted unless automatically inserted
src = "a + b\n"
if _, err := ParseExpr(src); err != nil {
t.Errorf("ParseExpr(%q): got error %s", src, err)
}
src = "a + b;"
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
// various other stuff following a valid expression
const validExpr = "a + b"
const anything = "dh3*#D)#_"
for _, c := range "!)]};," {
src := validExpr + string(c) + anything
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
}
// ParseExpr must not crash
for _, src := range valids {
ParseExpr(src)
}
}
func TestColonEqualsScope(t *testing.T) {
f, err := ParseFile(nil, token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
for _, v := range as.Rhs {
id := v.(*ast.Ident)
if id.Obj != nil {
t.Errorf("rhs %s has Obj, should not", id.Name)
}
}
for _, v := range as.Lhs {
id := v.(*ast.Ident)
if id.Obj == nil {
t.Errorf("lhs %s does not have Obj, should", id.Name)
}
}
}
func TestVarScope(t *testing.T) {
f, err := ParseFile(nil, token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
for _, v := range as.Values {
id := v.(*ast.Ident)
if id.Obj != nil {
t.Errorf("rhs %s has Obj, should not", id.Name)
}
}
for _, id := range as.Names {
if id.Obj == nil {
t.Errorf("lhs %s does not have Obj, should", id.Name)
}
}
}
func TestObjects(t *testing.T) {
const src = `
package p
import fmt "fmt"
const pi = 3.14
type T struct{}
var x int
func f() { L: }
`
f, err := ParseFile(nil, token.NewFileSet(), "", src, 0)
if err != nil {
t.Fatal(err)
}
objects := map[string]ast.ObjKind{
"p": ast.Bad, // not in a scope
"fmt": ast.Bad, // not resolved yet
"pi": ast.Con,
"T": ast.Typ,
"x": ast.Var,
"int": ast.Bad, // not resolved yet
"f": ast.Fun,
"L": ast.Lbl,
}
ast.Inspect(f, func(n ast.Node) bool {
if ident, ok := n.(*ast.Ident); ok {
obj := ident.Obj
if obj == nil {
if objects[ident.Name] != ast.Bad {
t.Errorf("no object for %s", ident.Name)
}
return true
}
if obj.Name != ident.Name {
t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
}
kind := objects[ident.Name]
if obj.Kind != kind {
t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
}
}
return true
})
}
func TestUnresolved(t *testing.T) {
f, err := ParseFile(nil, token.NewFileSet(), "", `
package p
//
func f1a(int)
func f2a(byte, int, float)
func f3a(a, b int, c float)
func f4a(...complex)
func f5a(a s1a, b ...complex)
//
func f1b(*int)
func f2b([]byte, (int), *float)
func f3b(a, b *int, c []float)
func f4b(...*complex)
func f5b(a s1a, b ...[]complex)
//
type s1a struct { int }
type s2a struct { byte; int; s1a }
type s3a struct { a, b int; c float }
//
type s1b struct { *int }
type s2b struct { byte; int; *float }
type s3b struct { a, b *s3b; c []float }
`, 0)
if err != nil {
t.Fatal(err)
}
want := "int " + // f1a
"byte int float " + // f2a
"int float " + // f3a
"complex " + // f4a
"complex " + // f5a
//
"int " + // f1b
"byte int float " + // f2b
"int float " + // f3b
"complex " + // f4b
"complex " + // f5b
//
"int " + // s1a
"byte int " + // s2a
"int float " + // s3a
//
"int " + // s1a
"byte int float " + // s2a
"float " // s3a
// collect unresolved identifiers
var buf bytes.Buffer
for _, u := range f.Unresolved {
buf.WriteString(u.Name)
buf.WriteByte(' ')
}
got := buf.String()
if got != want {
t.Errorf("\ngot: %s\nwant: %s", got, want)
}
}
var imports = map[string]bool{
`"a"`: true,
"`a`": true,
`"a/b"`: true,
`"a.b"`: true,
`"m\x61th"`: true,
`"greek/αβ"`: true,
`""`: false,
// Each of these pairs tests both `` vs "" strings
// and also use of invalid characters spelled out as
// escape sequences and written directly.
// For example `"\x00"` tests import "\x00"
// while "`\x00`" tests import `<actual-NUL-byte>`.
`"\x00"`: false,
"`\x00`": false,
`"\x7f"`: false,
"`\x7f`": false,
`"a!"`: false,
"`a!`": false,
`"a b"`: false,
"`a b`": false,
`"a\\b"`: false,
"`a\\b`": false,
"\"`a`\"": false,
"`\"a\"`": false,
`"\x80\x80"`: false,
"`\x80\x80`": false,
`"\xFFFD"`: false,
"`\xFFFD`": false,
}
func TestImports(t *testing.T) {
for path, isValid := range imports {
src := fmt.Sprintf("package p; import %s", path)
_, err := ParseFile(nil, token.NewFileSet(), "", src, 0)
switch {
case err != nil && isValid:
t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
case err == nil && !isValid:
t.Errorf("ParseFile(%s): got no error; expected one", src)
}
}
}
func TestCommentGroups(t *testing.T) {
f, err := ParseFile(nil, token.NewFileSet(), "", `
package p /* 1a */ /* 1b */ /* 1c */ // 1d
/* 2a
*/
// 2b
const pi = 3.1415
/* 3a */ // 3b
/* 3c */ const e = 2.7182
// Example from issue 3139
func ExampleCount() {
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
// Output:
// 3
// 5
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
expected := [][]string{
{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
{"/* 2a\n*/", "// 2b"},
{"/* 3a */", "// 3b", "/* 3c */"},
{"// Example from issue 3139"},
{"// before & after each rune"},
{"// Output:", "// 3", "// 5"},
}
if len(f.Comments) != len(expected) {
t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
}
for i, exp := range expected {
got := f.Comments[i].List
if len(got) != len(exp) {
t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
continue
}
for j, exp := range exp {
got := got[j].Text
if got != exp {
t.Errorf("got %q in group %d; expected %q", got, i, exp)
}
}
}
}
func getField(file *ast.File, fieldname string) *ast.Field {
parts := strings.Split(fieldname, ".")
for _, d := range file.Decls {
if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
for _, s := range d.Specs {
if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
if s, ok := s.Type.(*ast.StructType); ok {
for _, f := range s.Fields.List {
for _, name := range f.Names {
if name.Name == parts[1] {
return f
}
}
}
}
}
}
}
}
return nil
}
// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
func commentText(c *ast.CommentGroup) string {
var buf bytes.Buffer
if c != nil {
for _, c := range c.List {
buf.WriteString(c.Text)
}
}
return buf.String()
}
func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
f := getField(file, fieldname)
if f == nil {
t.Fatalf("field not found: %s", fieldname)
}
if got := commentText(f.Doc); got != lead {
t.Errorf("got lead comment %q; expected %q", got, lead)
}
if got := commentText(f.Comment); got != line {
t.Errorf("got line comment %q; expected %q", got, line)
}
}
func TestLeadAndLineComments(t *testing.T) {
f, err := ParseFile(nil, token.NewFileSet(), "", `
package p
type T struct {
/* F1 lead comment */
//
F1 int /* F1 */ // line comment
// F2 lead
// comment
F2 int // F2 line comment
// f3 lead comment
f3 int // f3 line comment
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
ast.FileExports(f)
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
if getField(f, "T.f3") != nil {
t.Error("not expected to find T.f3")
}
}
// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
func TestIssue9979(t *testing.T) {
for _, src := range []string{
"package p; func f() {;}",
"package p; func f() {L:}",
"package p; func f() {L:;}",
"package p; func f() {L:\n}",
"package p; func f() {L:\n;}",
"package p; func f() { ; }",
"package p; func f() { L: }",
"package p; func f() { L: ; }",
"package p; func f() { L: \n}",
"package p; func f() { L: \n; }",
} {
fset := token.NewFileSet()
f, err := ParseFile(nil, fset, "", src, 0)
if err != nil {
t.Fatal(err)
}
var pos, end token.Pos
ast.Inspect(f, func(x ast.Node) bool {
switch s := x.(type) {
case *ast.BlockStmt:
pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
case *ast.LabeledStmt:
pos, end = s.Pos()+2, s.End() // exclude "L:"
case *ast.EmptyStmt:
// check containment
if s.Pos() < pos || s.End() > end {
t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
}
// check semicolon
offs := fset.Position(s.Pos()).Offset
if ch := src[offs]; ch != ';' != s.Implicit {
want := "want ';'"
if s.Implicit {
want = "but ';' is implicit"
}
t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
}
}
return true
})
}
}
// TestIncompleteSelection ensures that an incomplete selector
// expression is parsed as a (blank) *ast.SelectorExpr, not a
// *ast.BadExpr.
func TestIncompleteSelection(t *testing.T) {
for _, src := range []string{
"package p; var _ = fmt.", // at EOF
"package p; var _ = fmt.\ntype X int", // not at EOF
} {
fset := token.NewFileSet()
f, err := ParseFile(nil, fset, "", src, 0)
if err == nil {
t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
continue
}
const wantErr = "expected selector or type assertion"
if !strings.Contains(err.Error(), wantErr) {
t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
}
var sel *ast.SelectorExpr
ast.Inspect(f, func(n ast.Node) bool {
if n, ok := n.(*ast.SelectorExpr); ok {
sel = n
}
return true
})
if sel == nil {
t.Error("found no *ast.SelectorExpr")
continue
}
const wantSel = "&{fmt _}"
if fmt.Sprint(sel) != wantSel {
t.Errorf("found selector %s, want %s", sel, wantSel)
continue
}
}
}
func TestLastLineComment(t *testing.T) {
const src = `package main
type x int // comment
`
fset := token.NewFileSet()
f, err := ParseFile(nil, fset, "", src, ParseComments)
if err != nil {
t.Fatal(err)
}
comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
if comment != "// comment" {
t.Errorf("got %q, want %q", comment, "// comment")
}
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains test cases for short valid and invalid programs.
package parser
import "testing"
var valids = []string{
"package p\n",
`package p;`,
`package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`,
`package p; func f() { if f(T{}) {} };`,
`package p; func f(func() func() func());`,
`package p; func f(...T);`,
`package p; func f(float, ...int);`,
`package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`,
`package p; func f(int,) {};`,
`package p; func f(...int,) {};`,
`package p; func f(x ...int,) {};`,
`package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`,
`package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
`package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
`package p; var a = T{{1, 2}, {3, 4}}`,
`package p; func f() { if ; true {} };`,
`package p; func f() { switch ; {} };`,
`package p; func f() { for _ = range "foo" + "bar" {} };`,
`package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`,
`package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,
`package p; func ((T),) m() {}`,
`package p; func ((*T),) m() {}`,
`package p; func (*(T),) m() {}`,
`package p; func _(x []int) { for range x {} }`,
`package p; func _() { if [T{}.n]int{} {} }`,
`package p; func _() { map[int]int{}[0]++; map[int]int{}[0] += 1 }`,
`package p; func _(x interface{f()}) { interface{f()}(x).f() }`,
`package p; const (x = 0; y; z)`, // issue 9639
`package p; var _ = map[P]int{P{}:0, {}:1}`,
`package p; var _ = map[*P]int{&P{}:0, {}:1}`,
`package p; type T = int`,
`package p; type (T = p.T; _ = struct{}; x = *T)`,
}
func TestValid(t *testing.T) {
for _, src := range valids {
checkErrors(nil, t, src, src)
}
}
var invalids = []string{
// `foo /* ERROR "expected 'package'" */ !`,
`package p; func f() { if { /* ERROR "missing condition" */ } };`,
`package p; func f() { if ; /* ERROR "missing condition" */ {} };`,
`package p; func f() { if f(); /* ERROR "missing condition" */ {} };`,
`package p; func f() { if _ = range /* ERROR "expected operand" */ x; true {} };`,
`package p; func f() { switch _ /* ERROR "expected switch expression" */ = range x; true {} };`,
`package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`,
`package p; func f() { for ; ; _ = range /* ERROR "expected operand" */ x {} };`,
`package p; func f() { for ; _ /* ERROR "expected boolean or range expression" */ = range x ; {} };`,
`package p; func f() { switch t = /* ERROR "expected ':=', found '='" */ t.(type) {} };`,
`package p; func f() { switch t /* ERROR "expected switch expression" */ , t = t.(type) {} };`,
`package p; func f() { switch t /* ERROR "expected switch expression" */ = t.(type), t {} };`,
`package p; var a = [ /* ERROR "expected expression" */ 1]int;`,
`package p; var a = [ /* ERROR "expected expression" */ ...]int;`,
`package p; var a = struct /* ERROR "expected expression" */ {}`,
`package p; var a = func /* ERROR "expected expression" */ ();`,
`package p; var a = interface /* ERROR "expected expression" */ {}`,
`package p; var a = [ /* ERROR "expected expression" */ ]int`,
`package p; var a = map /* ERROR "expected expression" */ [int]int`,
`package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`,
`package p; var a = ( /* ERROR "expected expression" */ []int);`,
`package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`,
`package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`,
`package p; func f() { if x := g(); x /* ERROR "expected boolean expression" */ = 0 {}};`,
`package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`,
`package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`,
`package p; func f() { var s []int; _ = s[i:j: /* ERROR "3rd index required" */ ] };`,
`package p; func f() { var s []int; _ = s[i: /* ERROR "2nd index required" */ :k] };`,
`package p; func f() { var s []int; _ = s[i: /* ERROR "2nd index required" */ :] };`,
`package p; func f() { var s []int; _ = s[: /* ERROR "2nd index required" */ :] };`,
`package p; func f() { var s []int; _ = s[: /* ERROR "2nd index required" */ ::] };`,
`package p; func f() { var s []int; _ = s[i:j:k: /* ERROR "expected ']'" */ l] };`,
`package p; func f() { for x /* ERROR "boolean or range expression" */ = []string {} }`,
`package p; func f() { for x /* ERROR "boolean or range expression" */ := []string {} }`,
`package p; func f() { for i /* ERROR "boolean or range expression" */ , x = []string {} }`,
`package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
// issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
// issue 9639
`package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`,
`package p; const x /* ERROR "missing constant value" */ ;`,
`package p; const x /* ERROR "missing constant value" */ int;`,
`package p; const (x = 0; y; z /* ERROR "missing constant value" */ int);`,
// issue 12437
`package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ }{};`,
`package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{};`,
// issue 11611
`package p; type _ struct { int, } /* ERROR "expected type, found '}'" */ ;`,
`package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`,
`package p; type _ struct { ( /* ERROR "expected anonymous field" */ int) };`,
`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
// issue 13475
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
`package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`,
}
func TestInvalid(t *testing.T) {
for _, src := range invalids {
checkErrors(nil, t, src, src)
}
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package printer_test
import (
"bytes"
"fmt"
"strings"
"testing"
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/printer"
"wa-lang.org/wa/internal/token"
)
// Dummy test function so that godoc does not use the entire file as example.
func Test(*testing.T) {}
func parseFunc(filename, functionname string) (fun *ast.FuncDecl, fset *token.FileSet) {
fset = token.NewFileSet()
if file, err := parser.ParseFile(nil, fset, filename, nil, 0); err == nil {
for _, d := range file.Decls {
if f, ok := d.(*ast.FuncDecl); ok && f.Name.Name == functionname {
fun = f
return
}
}
}
panic("function not found")
}
func ExampleFprint() {
// Parse source file and extract the AST without comments for
// this function, with position information referring to the
// file set fset.
funcAST, fset := parseFunc("example_test.go", "ExampleFprint")
// Print the function body into buffer buf.
// The file set is provided to the printer so that it knows
// about the original source formatting and can add additional
// line breaks where they were present in the source.
var buf bytes.Buffer
printer.Fprint(&buf, fset, funcAST.Body)
// Remove braces {} enclosing the function body, unindent,
// and trim leading and trailing white space.
s := buf.String()
s = s[1 : len(s)-1]
s = strings.TrimSpace(strings.ReplaceAll(s, "\n\t", "\n"))
// Print the cleaned-up body text to stdout.
fmt.Println(s)
// output:
// funcAST, fset := parseFunc("example_test.go", "ExampleFprint")
//
// var buf bytes.Buffer
// printer.Fprint(&buf, fset, funcAST.Body)
//
// s := buf.String()
// s = s[1 : len(s)-1]
// s = strings.TrimSpace(strings.ReplaceAll(s, "\n\t", "\n"))
//
// fmt.Println(s)
}
......@@ -469,10 +469,10 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
}
p.expr(x)
}
p.print(token.COLON)
if len(f.Names) > 0 {
p.print(blank)
}
p.print(token.COLON)
p.expr(f.Type)
} else { // interface
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
......@@ -512,8 +512,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
if len(f.Names) > 0 {
// named fields
p.identList(f.Names, false)
p.print(sep)
p.print(token.COLON)
p.print(sep)
p.expr(f.Type)
extraTabs = 1
} else {
......@@ -1401,6 +1401,7 @@ func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
extraTabs--
}
if s.Type != nil {
p.print(token.COLON)
p.expr(s.Type)
}
if s.Values != nil {
......@@ -1523,7 +1524,7 @@ func (p *printer) genDecl(d *ast.GenDecl) {
p.print(d.Lparen, token.LPAREN)
if n := len(d.Specs); n > 0 {
p.print(indent, formfeed)
if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR || d.Tok == token.GLOBAL) {
// two or more grouped const/var declarations:
// determine if the type column must be kept
keepType := keepTypeColumn(d.Specs)
......
此差异已折叠。
......@@ -164,6 +164,7 @@ var tokens = [...]elt{
{token.FOR, "for", keyword},
{token.FUNC, "func", keyword},
{token.GLOBAL, "global", keyword},
{token.IF, "if", keyword},
{token.IMPORT, "import", keyword},
......
......@@ -1722,7 +1722,7 @@ start:
case *ast.DeclStmt: // Con, Var or Typ
d := s.Decl.(*ast.GenDecl)
if d.Tok == token.VAR {
if d.Tok == token.VAR || d.Tok == token.GLOBAL {
for _, spec := range d.Specs {
if vs, ok := spec.(*ast.ValueSpec); ok {
b.localValueSpec(fn, vs)
......
......@@ -126,7 +126,7 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
}
}
case token.VAR:
case token.VAR, token.GLOBAL:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) {
......
......@@ -77,7 +77,7 @@ func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
if (decl.Tok == token.VAR || decl.Tok == token.GLOBAL) && n >= 3 {
// Package-level 'var' initializer.
return pkg.init
}
......
......@@ -615,7 +615,7 @@ func (check *Checker) declStmt(decl ast.Decl) {
check.declare(check.scope, name, lhs[i], scopePos)
}
case token.VAR:
case token.VAR, token.GLOBAL:
top := len(check.delayed)
lhs0 := make([]*Var, len(s.Names))
......
......@@ -124,7 +124,7 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele
stmtBranches = func(s ast.Stmt) {
switch s := s.(type) {
case *ast.DeclStmt:
if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
if d, _ := s.Decl.(*ast.GenDecl); d != nil && (d.Tok == token.VAR || d.Tok == token.GLOBAL) {
recordVarDecl(d.Pos())
}
......
......@@ -356,7 +356,7 @@ func (check *Checker) collectObjects() {
check.arityMatch(s, last)
case token.VAR:
case token.VAR, token.GLOBAL:
lhs := make([]*Var, len(s.Names))
// If there's exactly one rhs initializer, use
// the same declInfo d1 for all lhs variables
......
// 版权 @2023 凹语言 作者。保留所有权利。
package wamime
import (
"strings"
"wa-lang.org/wa/internal/scanner"
"wa-lang.org/wa/internal/token"
)
const mimePrefix0 = "#syntax=" // 旧语法, 先保持兼容
const mimePrefix = "#wa:syntax=" // #wa:syntax=wa, #wa:syntax=wz
func GetCodeMime(filename string, code []byte) string {
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile(filename, fset.Base(), len(code))
s.Init(file, code, nil, scanner.ScanComments)
// 解析 #wa:syntax=xx
for {
_, tok, lit := s.Scan()
if tok != token.COMMENT {
break
}
// 旧语法 #syntax=
if strings.HasPrefix(lit, mimePrefix0) {
if mime := lit[len(mimePrefix0):]; mime != "" {
return mime
}
return ""
}
// 新语法 #wa:syntax=
if strings.HasPrefix(lit, mimePrefix) {
if mime := lit[len(mimePrefix):]; mime != "" {
return mime
}
return ""
}
}
// 根据文件名后缀解析
if i := strings.LastIndex(filename, "."); i > 0 {
if s := filename[i+1:]; s != "" {
return s
}
}
// 未知类型
return ""
}
// 版权 @2023 凹语言 作者。保留所有权利。
package wamime
import (
"testing"
)
func TestGetCodeMime(t *testing.T) {
for i, tx := range tests {
got := GetCodeMime(tx.filename, []byte(tx.code))
expect := tx.mime
if got != expect {
t.Fatalf("%d: expect =%q, got = %q", i, expect, got)
}
}
}
var tests = []struct {
filename string
code string
mime string
}{
{"-", "", ""},
{"prog.wa", "", "wa"},
{"prog.wz", "", "wz"},
{"x.wa", "#", "wa"},
{"", "#wa:syntax=wx", "wx"},
{
"",
`// 版权 @2019 凹语言 作者。保留所有权利。
#wa:syntax=wa
import "fmt"
import "runtime"
global year: i32 = 2023
func main {
println("你好,凹语言!", runtime.WAOS)
println(add(40, 2), year)
fmt.Println("1+1 =", 1+1)
}
func add(a: i32, b: i32) => i32 {
return a+b
}
`,
"wa",
},
{
"",
`// 版权 @2019 凹语言 作者。保留所有权利。
#wa:syntax=wz
引于 "书"
【启】:
书·说:"你好,凹语言中文版!"
`,
"wz",
},
}
......@@ -156,6 +156,14 @@ func (p *Module) buildModule() error {
p.wazeroInitErr = err
return err
}
case config.WaOS_mvp:
if _, err = MvpInstantiate(p.wazeroCtx, p.wazeroRuntime); err != nil {
p.wazeroInitErr = err
return err
}
default:
return fmt.Errorf("unknown waos: %q", p.cfg.WaOS)
}
return nil
......
// 版权 @2022 凹语言 作者。保留所有权利。
package wazero
import (
"context"
"fmt"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"wa-lang.org/wa/internal/config"
)
func MvpInstantiate(ctx context.Context, rt wazero.Runtime) (api.Closer, error) {
return rt.NewHostModuleBuilder(config.WaOS_mvp).
// func waPrintI32(v: i32)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v int32) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintI32").
// func waPrintU32(v: u32)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v uint32) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintU32").
// func waPrintI64(v: i64)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v int64) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintI64").
// func waPrintU64(v: u64)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v uint64) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintU64").
// func waPrintF32(v: f32)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v float32) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintF32").
// func waPrintF64(v: f64)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, v float64) {
fmt.Print(v)
}).
WithParameterNames("v").
Export("waPrintF64").
// func waPrintRune(ch: i32)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, ch uint32) {
fmt.Printf("%c", rune(ch))
}).
WithParameterNames("ch").
Export("waPrintRune").
// func waPuts(ptr: i32, len: i32)
NewFunctionBuilder().
WithFunc(func(ctx context.Context, m api.Module, ptr, len uint32) {
bytes, _ := m.Memory().Read(ctx, ptr, len)
fmt.Print(string(bytes))
}).
WithParameterNames("ptr", "len").
Export("waPuts").
// Done
Instantiate(ctx, rt)
}
v0.8.0-dev
\ No newline at end of file
......@@ -3,6 +3,9 @@
default:
ci-test-all:
@echo "== fmt examples/... =="
go run ../../main.go fmt ./...
@echo "== examples test begin =="
# loop forever
......@@ -12,6 +15,7 @@ ci-test-all:
cd ./brainfuck && make
cd ./expr && make
cd ./hello && make
cd ./misc && make
cd ./pkg && make
cd ./prime && make
cd ./reftoptr && make
......
......@@ -8,10 +8,10 @@ func main() {
}
type BrainFuck struct {
mem :[30000]byte
code :string
pos :int
pc :int
mem: [30000]byte
code: string
pos: int
pc: int
}
func NewBrainFuck(code: string) => *BrainFuck {
......
......@@ -2,10 +2,10 @@
// BF 虚拟机
type BrainFuck struct {
mem :[30000]byte
code :string
pos :int
pc :int
mem: [30000]byte
code: string
pos: int
pc: int
}
func NewBrainFuck(code: string) => *BrainFuck {
......
// 版权 @2021 凹语言 作者。保留所有权利。
var s = []int{1, 2, 3, 4, 5, 6}
func main() {
d := make([]int, 3)
copy(d, s)
for i, v := range d {
println("d[", i, "]=", v)
}
a := s[0:4]
a := s[0:4]
b := s[1:5]
copy(a, b)
for i, v := range s {
println("s[", i, "]=", v)
}
copy(b, a)
for i, v := range s {
println("s[", i, "]=", v)
}
}
\ No newline at end of file
}
......@@ -7,9 +7,9 @@ func main {
println("你好,凹语言!", runtime.WAOS)
println(add(40, 2))
fmt.Println(1+1)
fmt.Println(1 + 1)
}
func add(a: i32, b: i32) => i32 {
return a+b
return a + b
}
......@@ -5,7 +5,7 @@ type T1 struct {
b: string
}
func T1.print(){
func T1.print() {
println("a: ", this.a)
}
......@@ -18,8 +18,8 @@ type T2 struct {
}
func main() {
v1, v2: T1
var v1, v2: T1
v1.a = 13
v2.a = 42
if v1 == v2 {
......@@ -27,21 +27,21 @@ func main() {
} else {
println("ne")
}
v2.a = 13
if v1 == v2 {
println("eq")
} else {
println("ne")
}
v1.b = "abc"
if v1 == v2 {
println("eq")
} else {
println("ne")
}
v2.b = "abc"
if v1 == v2 {
println("eq")
......@@ -49,20 +49,20 @@ func main() {
println("ne")
}
i1, i2: interface{}
var i1, i2: interface{}
i1 = "abc"
if i1 == nil{
if i1 == nil {
println("i1 == nil:eq")
} else {
println("i1 == nil:ne")
}
i1 = nil
if i1 == nil{
if i1 == nil {
println("i1 == nil:eq")
} else {
println("i1 == nil:ne")
}
}
i1 = i32(13)
i2 = i32(42)
if i1 == i2 {
......@@ -76,20 +76,20 @@ func main() {
} else {
println("ne")
}
i2 = "abc"
if i1 == i2 {
println("eq")
} else {
println("ne")
}
i1 = "abc"
i1 = "abc"
if i1 == i2 {
println("eq")
} else {
println("ne")
}
i1 = v1
if i1 == i2 {
println("eq")
......@@ -102,35 +102,33 @@ func main() {
} else {
println("ne")
}
i3: I
var i3: I
i3 = &v1
if i1 == i3 {
if i1 == i3 {
println("eq")
} else {
println("ne")
}
i1 = &v1
if i1 == i3 {
if i1 == i3 {
println("eq")
} else {
println("ne")
}
v3, v4: T2
var v3, v4: T2
//if v3 == v4 {
// println("eq")
//} else {
// println("ne")
//}
i1 = v3
i2 = v4
if i1 == i2 { //panic
if i1 == i2 { //panic
println("eq")
} else {
println("ne")
}
}
......@@ -7,13 +7,13 @@
import "strconv" => __yystrconv__
type exprSymType struct {
yys :int
num :int
yys: int
num: int
}
const NUM = 57346
var exprToknames = [...]string{
global exprToknames = [...]string{
"$end",
"error",
"$unk",
......@@ -26,7 +26,7 @@ var exprToknames = [...]string{
"NUM",
}
var exprStatenames = [...]string{}
global exprStatenames = [...]string{}
const exprEofCode = 1
const exprErrCode = 2
......@@ -36,13 +36,13 @@ const exprInitialStackSize = 16
const eof = 0
type exprToken struct {
Kind :int
Value :int
Kind: int
Value: int
}
type exprLexer struct {
tokens :[]exprToken
pos :int
tokens: []exprToken
pos: int
}
func exprLexer.Lex(yylval: *exprSymType) => int {
......@@ -79,7 +79,7 @@ func main {
})
}
var exprExca = [...]int{
global exprExca = [...]int{
-1, 1,
1, -1,
-2, 0,
......@@ -89,45 +89,45 @@ const exprPrivate = 57344
const exprLast = 23
var exprAct = [...]int{
global exprAct = [...]int{
7, 4, 5, 2, 21, 9, 6, 8, 12, 13,
9, 1, 8, 16, 3, 19, 20, 17, 18, 14,
15, 10, 11,
}
var exprPact = [...]int{
global exprPact = [...]int{
-3, -1000, -1000, 17, -3, -3, 13, -1000, -1000, -3,
2, 2, -1000, -1000, 2, 2, -5, 13, 13, -1000,
-1000, -1000,
}
var exprPgo = [...]int{
global exprPgo = [...]int{
0, 3, 14, 6, 0, 11,
}
var exprR1 = [...]int{
global exprR1 = [...]int{
0, 5, 1, 1, 1, 2, 2, 2, 3, 3,
3, 4, 4,
}
var exprR2 = [...]int{
global exprR2 = [...]int{
0, 1, 1, 2, 2, 1, 3, 3, 1, 3,
3, 1, 3,
}
var exprChk = [...]int{
global exprChk = [...]int{
-1000, -5, -1, -2, 4, 5, -3, -4, 10, 8,
4, 5, -1, -1, 6, 7, -1, -3, -3, -4,
-4, 9,
}
var exprDef = [...]int{
global exprDef = [...]int{
0, -2, 1, 2, 0, 0, 5, 8, 11, 0,
0, 0, 3, 4, 0, 0, 0, 6, 7, 9,
10, 12,
}
var exprTok1 = [...]int{
global exprTok1 = [...]int{
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
......@@ -135,33 +135,33 @@ var exprTok1 = [...]int{
8, 9, 6, 4, 3, 5, 3, 7,
}
var exprTok2 = [...]int{
global exprTok2 = [...]int{
2, 3, 10,
}
var exprTok3 = [...]int{
global exprTok3 = [...]int{
0,
}
type exprErrorMessageInfo struct {
state :int
token :int
msg :string
state: int
token: int
msg: string
}
var exprErrorMessages = [...]exprErrorMessageInfo{}
global exprErrorMessages = [...]exprErrorMessageInfo{}
/* parser for yacc output */
var (
global (
exprDebug = 0
exprErrorVerbose = false
)
type exprParser struct {
lval :exprSymType
stack :[exprInitialStackSize]exprSymType
char :int
lval: exprSymType
stack: [exprInitialStackSize]exprSymType
char: int
}
func exprParser.Lookahead => int {
......
......@@ -5,8 +5,8 @@ import "3rdparty/pkg"
import "myapp/mymath"
func main {
fmt.Println(40+2)
pkg.Println(100+2)
fmt.Println(40 + 2)
pkg.Println(100 + 2)
println(mymath.I8Max)
println(sum(100))
......@@ -14,7 +14,7 @@ func main {
}
func sum(n: int) => int {
v: int
var v: int
for i := 1; i <= n; i++ {
v += i
}
......
......@@ -20,44 +20,44 @@ func T1.f() {
println("This is T1, this.a==", this.a)
}
func T2.f(){
func T2.f() {
println("This is T2, this.b==", this.b)
}
func main() {
v1 := T1{a: 13}
i1: I1 = &v1 //具体类型到具名接口
v1.f() //直接调用
i1.f() //接口调用
var i1: I1 = &v1 //具体类型到具名接口
v1.f() //直接调用
i1.f() //接口调用
doConcreteType(i1)
i1.f()
v2 := T2{b: 42}
i1 = &v2 //具体类型到具名接口
i1 = &v2 //具体类型到具名接口
i1.f()
doConcreteType(i1)
i1.f()
ni: interface{} = &v1 //具体类型到空接口
i1 = ni.(I1) //接口动态互转
var ni: interface{} = &v1 //具体类型到空接口
i1 = ni.(I1) //接口动态互转
i1.f()
ni = &v2 //具体类型到空接口
i1 = ni.(I1) //接口动态互转
ni = &v2 //具体类型到空接口
i1 = ni.(I1) //接口动态互转
i1.f()
ival: i32 = 777
var ival: i32 = 777
ni = ival
doConcreteType(ni)
doConcreteType(v1)
doConcreteType(v2)
doConcreteType("你好凹语言")
//i2 := ni.(I2) //接口互转,由于v2未实现I2,这会触发异常
//i2.f2()
anoni: interface{ f() } = &v1 //具体类型到匿名接口
var anoni: interface{ f() } = &v1 //具体类型到匿名接口
anoni.f()
i1 = anoni //匿名接口向具名接口转换
i1.f()
......@@ -65,25 +65,25 @@ func main() {
func doConcreteType(i: interface{}) {
//接口到具体类型断言
switch c := i.(type){
case *T1:
println("*T1")
c.a *= 2
case *T2:
println("*T2")
c.b *= 2
case i32:
println("i32: ", c)
case string:
println("string: ", c)
case T1:
println("T1, T1.a==", c.a)
case T2:
println("T2, T2.b==", c.b)
switch c := i.(type) {
case *T1:
println("*T1")
c.a *= 2
case *T2:
println("*T2")
c.b *= 2
case i32:
println("i32: ", c)
case string:
println("string: ", c)
case T1:
println("T1, T1.a==", c.a)
case T2:
println("T2, T2.b==", c.b)
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。