...
 
Commits (17)
    https://gitcode.net/wa-lang/wa/-/commit/ad1a12c93b48384d8fad6dd14814162e169ce983 屏蔽i8、i16类型 2023-06-19T15:11:43+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/b231910a567018b8cd59b0a279d508d1cab3f98b Merge branch 'backend_wasm' 2023-06-19T17:30:30+08:00 3dgen 476582@qq.com https://gitcode.net/wa-lang/wa/-/commit/013ecb93ec6bc4e6edd911676188aac6efb38617 初步支持 global, AST 暂时作为 var 处理 2023-06-19T21:10:22+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/0b18402827186cf21555f56129c6044f84281a6c Merge branch 'master' of gitee.com:wa-lang/wa 2023-06-19T21:11:41+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/bce3aefc362333be106285f71a43b65438ee7116 snake 避免使用 i8 类型, 暂不支持 2023-06-19T21:25:55+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/a21a345832dd333b69a0f8e23c5840d069bd7232 parser 屏蔽 i8 和 i16 2023-06-19T21:41:02+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/d93a368db5b8ed783dbd4b9aa75bc9d073efb107 包装 wabt 和 wazero 2023-06-20T23:20:22+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/de4655625f3c9683a29eddeed57e01071a7dbfb1 优化 api.RunCode 2023-06-21T07:18:09+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/a72a04eaec12f557a0a1cb9ac62f62271d14e422 启用新包装的 wabt 函数 2023-06-21T07:42:05+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/61d8ae100e196b4ddcf1ca74d0211cfd69aa6e67 wayacc 输出代码改用凹语言风格 2023-06-21T08:34:11+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/663c4ba3c87326523b323aff04c4f7f8ffe31c29 更新 readme 2023-06-21T08:36:18+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/4aa6f29533838a8757bff55067672f40eb6ffb2a 修复 run 子命令 2023-06-22T07:34:46+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/473e0fcfe6afee7584b273f9f46ba6ccf8e214ff 切换到新 waroot 目录, 删除旧目录 2023-06-22T09:56:12+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/5319e5a5eda1cc665f297b042c959b9857fab338 test 切换到 wazero 包装函数 2023-06-22T11:05:19+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/106cf4c8288bbabae2b0a86ed6bd717cf8b39b1f 完善测试功能 2023-06-22T14:10:47+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/3a416ed2ac3f789517ae5bccf929007d020dd8f3 重构 wa 命令代码目录结构 2023-06-22T19:21:46+08:00 chai2010 chaishushan@gmail.com https://gitcode.net/wa-lang/wa/-/commit/ca4bc40a3b743f3b9e0995dcd6f0a753dc5f0838 wa 命令行内置简版 play 2023-06-23T21:37:53+08:00 chai2010 chaishushan@gmail.com
......@@ -54,9 +54,11 @@
```wa
import "fmt"
global year: i32 = 2023
func main {
println("你好,凹语言!")
println(add(40, 2))
println("hello, Wa!")
println(add(40, 2), year)
fmt.Println(1+1)
}
......@@ -71,7 +73,7 @@ func add(a: i32, b: i32) => i32 {
```
$ go run main.go hello.wa
你好,凹语言!
42
42 2023
2
```
......@@ -155,4 +157,4 @@ $ cd waroot && go run ../main.go run examples/prime
|吴烜 | 3000|
|刘斌 | 2500|
贡献点变更记录见 [_cplog](_cplog) 目录。
贡献点变更记录见 [waroot/cplog](waroot/cplog) 目录。
......@@ -60,9 +60,11 @@ Print rune and call function:
```wa
import "fmt"
global year: i32 = 2023
func main {
println("hello, Wa!")
println(add(40, 2))
println(add(40, 2), year)
fmt.Println(1+1)
}
......@@ -77,7 +79,7 @@ Execute the program:
```
$ go run main.go hello.wa
hello, Wa!
42
42 2023
2
```
......
......@@ -6,27 +6,27 @@
package api
import (
"os"
"wa-lang.org/wa/internal/app/apputil"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/wabt"
"wa-lang.org/wa/internal/wazero"
)
// 执行凹代码
func RunCode(cfg *config.Config, filename, code string, arg ...string) (stdoutStderr []byte, err error) {
func RunCode(cfg *config.Config, filename, code string, args ...string) (stdoutStderr []byte, err error) {
// 编译为 wat 格式
wat, err := BuildFile(cfg, filename, code)
// wat 写到临时文件
outfile := "a.out.wat"
if !*FlagDebugMode {
defer os.Remove(outfile)
watBytes, err := BuildFile(cfg, filename, code)
if err != nil {
return
}
if err = os.WriteFile(outfile, []byte(wat), 0666); err != nil {
return nil, err
// wat 编译为 wasm
wasmBytes, err := wabt.Wat2Wasm(watBytes)
if err != nil {
return
}
// 执行 wat 文件
stdoutStderr, err = apputil.RunWasm(cfg, outfile, arg...)
// main 执行
stdout, stderr, err := wazero.RunWasm(cfg, filename, wasmBytes, args...)
stdoutStderr = append(stdout, stderr...)
return
}
......@@ -11,7 +11,7 @@ import (
func ExampleRunCode() {
const code = `
var gBase: i32 = 1000
global gBase: i32 = 1000
func main() {
println(add(40, 2) + gBase)
......
......@@ -31,7 +31,7 @@ func main() {
}
const code = `
func main() {
func main {
println(add(40, 2))
}
......
......@@ -3,11 +3,22 @@
package app
import (
"fmt"
"os/exec"
"runtime"
"wa-lang.org/wa/internal/app/appfmt"
"wa-lang.org/wa/internal/app/appinit"
"wa-lang.org/wa/internal/app/applex"
"wa-lang.org/wa/internal/app/appplay"
"wa-lang.org/wa/internal/app/apptest"
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/backends/compiler_c"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/loader"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/token"
)
// 命令行程序对象
......@@ -58,3 +69,47 @@ func NewApp(opt *Option) *App {
func (p *App) GetConfig() *config.Config {
return p.opt.Config()
}
func (p *App) RunTest(pkgpath string, appArgs ...string) {
apptest.RunTest(p.opt.Config(), pkgpath, appArgs...)
}
func (p *App) AST(filename string) error {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(nil, fset, filename, nil, 0)
if err != nil {
return err
}
return ast.Print(fset, f)
}
func (p *App) CIR(filename string) error {
cfg := p.opt.Config()
prog, err := loader.LoadProgram(cfg, filename)
if err != nil {
return err
}
var c compiler_c.CompilerC
c.CompilePackage(prog.SSAMainPkg)
fmt.Println(c.String())
return nil
}
func (p *App) Fmt(path string) error {
return appfmt.Fmt(path)
}
func (p *App) Playground(addr string) error {
return appplay.RunPlayground(addr)
}
func (p *App) InitApp(name, pkgpath string, update bool) error {
return appinit.InitApp(name, pkgpath, update)
}
func (p *App) Lex(filename string) error {
return applex.Lex(filename)
}
// 版权 @2023 凹语言 作者。保留所有权利。
package app
import (
"wa-lang.org/wa/internal/ast"
"wa-lang.org/wa/internal/parser"
"wa-lang.org/wa/internal/token"
)
func (p *App) AST(filename string) error {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(nil, fset, filename, nil, 0)
if err != nil {
return err
}
return ast.Print(fset, f)
}
// 版权 @2019 凹语言 作者。保留所有权利。
package app
import (
"wa-lang.org/wa/internal/backends/compiler_c"
"wa-lang.org/wa/internal/loader"
)
func (p *App) CIR(filename string) error {
cfg := p.opt.Config()
prog, err := loader.LoadProgram(cfg, filename)
if err != nil {
return err
}
var c compiler_c.CompilerC
c.CompilePackage(prog.SSAMainPkg)
print("\n\n")
print(c.String())
return nil
}
// 版权 @2023 凹语言 作者。保留所有权利。
package app
package appfmt
import (
"fmt"
......@@ -12,7 +12,7 @@ import (
"wa-lang.org/wa/internal/format"
)
func (p *App) Fmt(path string) error {
func Fmt(path string) error {
if path == "" {
path, _ = os.Getwd()
}
......@@ -24,7 +24,7 @@ func (p *App) Fmt(path string) error {
var changedFileList []string
for _, s := range waFileList {
changed, err := p.fmtFile(s)
changed, err := fmtFile(s)
if err != nil {
return err
}
......@@ -38,7 +38,7 @@ func (p *App) Fmt(path string) error {
return nil
}
func (p *App) fmtFile(path string) (changed bool, err error) {
func fmtFile(path string) (changed bool, err error) {
code, changed, err := format.File(nil, path, nil)
if err != nil {
return false, err
......
// 版权 @2023 凹语言 作者。保留所有权利。
package app
package appinit
import (
"fmt"
......@@ -10,10 +10,10 @@ import (
"text/template"
"time"
"wa-lang.org/wa/internal/waroot"
"wa-lang.org/wa/waroot"
)
func (p *App) InitApp(name, pkgpath string, update bool) error {
func InitApp(name, pkgpath string, update bool) error {
if name == "" {
return fmt.Errorf("init failed: <%s> is empty", name)
}
......
// 版权 @2023 凹语言 作者。保留所有权利。
package app
package applex
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"wa-lang.org/wa/internal/scanner"
"wa-lang.org/wa/internal/token"
)
func (p *App) Lex(filename string) error {
src, err := p.readSource(filename, nil)
func Lex(filename string) error {
src, err := readSource(filename, nil)
if err != nil {
return err
}
......@@ -30,3 +34,25 @@ func (p *App) 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
}
<!doctype html>
<html>
<head>
<link rel="icon" href="/static/logo.svg">
<title>凹语言游乐场</title>
<link rel="stylesheet" href="/static/playground/playground-full.css">
<script src="/static/playground/jquery/1.8.2/jquery.min.js"></script>
<script src="/static/playground/playground-full.js"></script>
<style>
.cm-tab {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);
background-position: right;
background-repeat: no-repeat;
}
</style>
<script>
$(document).ready(function() {
playground({
'theme': 'default',
'codeEl': '#code',
'outputEl': '#output',
'runEl': '#run',
'fmtEl': '#fmt',
'enableHistory': true
});
// Avoid line wrapping.
var about = $('#about');
about.click(function(e) {
if ($(e.target).is('a')) {
return;
}
about.hide();
});
$('#homeButton').click(function() {
$(location).attr('href','/')
})
$('#aboutButton').click(function() {
if (about.is(':visible')) {
about.hide();
return;
}
about.show();
})
// Fire Google Analytics events for Run/Share button clicks.
if (window.trackEvent) {
$('#run').click(function() {
window.trackEvent('playground', 'click', 'run-button');
});
$('#fmt').click(function() {
window.trackEvent('playground', 'click', 'fmt-button');
});
}
});
</script>
</head>
<body itemscope itemtype="http://schema.org/CreativeWork">
<div id="banner">
<div id="head" itemprop="name">凹语言游乐场</div>
<div id="controls">
<input type="button" value="运行" id="run">
<input type="button" value="格式化" id="fmt">
</div>
<div id="aboutControls">
<input type="button" value="关于" id="aboutButton">
</div>
</div>
<div id="wrap">
<textarea itemprop="description" id="code" name="code" autocorrect="off" autocomplete="off" autocapitalize="off" spellcheck="false">{{printf "%s" .Snippet.Body}}</textarea>
</div>
<div id="output"></div>
<div id="about">
<p><b>凹语言 (The Wa Programming Language)</b></p>
<a target="_blank" href="https://wa-lang.org">https://wa-lang.org</a>
</div>
</body>
</html>
<!--
// 版权 @2019 凹语言 作者。保留所有权利。
// https://wa-lang.org
-->
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 400" width="400" height="400"
style="border: 0px solid red"
>
<path
stroke="none" fill="LightSeaGreen"
d="M60,50 h90 v100 h100 v-100 h90 l10,10 v280 l-10,10 h-280 l-10,-10 v-280"
/>
<path
stroke="none" fill="white"
d="M100,100 m-5,-5 h10 v10 h-10"
/>
<path
stroke="none" fill="white"
d="M400,100 m-100,0 m-5,-5 h10 v10 h-10"
/>
<path
stroke="white" fill="none" stroke-width="8" stroke-linecap="round"
d="M200,230 l34,34 l34,-34 M200,230 l-34,34 l-34,-34"
/>
</svg>
build:
cat codemirror.js codemirror-wa.js playground.js | uglifyjs > playground-full.js && \
cat style.css codemirror.css codemirror-*.css > playground-full.css
/*
Name: Base16 Default Light
Author: Chris Kempson (http://chriskempson.com)
CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
*/
.cm-s-base16-light.CodeMirror { background: #f5f5f5; color: #202020; }
.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 0px; }
.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; }
.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; }
.cm-s-base16-light .CodeMirror-linenumber { color: #b0b0b0; }
.cm-s-base16-light .CodeMirror-cursor { border-left: 1px solid #505050; }
.cm-s-base16-light span.cm-comment { color: #8f5536; }
.cm-s-base16-light span.cm-atom { color: #aa759f; }
.cm-s-base16-light span.cm-number { color: #aa759f; }
.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; }
.cm-s-base16-light span.cm-keyword { color: #ac4142; }
.cm-s-base16-light span.cm-string { color: #f4bf75; }
.cm-s-base16-light span.cm-variable { color: #90a959; }
.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
.cm-s-base16-light span.cm-def { color: #d28445; }
.cm-s-base16-light span.cm-bracket { color: #202020; }
.cm-s-base16-light span.cm-tag { color: #ac4142; }
.cm-s-base16-light span.cm-link { color: #aa759f; }
.cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; }
.cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; }
.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
/*
Name: material
Author: Michael Kaminsky (http://github.com/mkaminsky11)
Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme)
*/
.cm-s-material.CodeMirror {
background-color: #263238;
color: rgba(233, 237, 237, 1);
}
.cm-s-material .CodeMirror-gutters {
background: #263238;
color: rgb(83,127,126);
border: none;
}
.cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { color: rgb(83,127,126); }
.cm-s-material .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
.cm-s-material div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); }
.cm-s-material.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0); }
.cm-s-material .cm-keyword { color: rgba(199, 146, 234, 1); }
.cm-s-material .cm-operator { color: rgba(233, 237, 237, 1); }
.cm-s-material .cm-variable-2 { color: #80CBC4; }
.cm-s-material .cm-variable-3 { color: #82B1FF; }
.cm-s-material .cm-builtin { color: #DECB6B; }
.cm-s-material .cm-atom { color: #F77669; }
.cm-s-material .cm-number { color: #F77669; }
.cm-s-material .cm-def { color: rgba(233, 237, 237, 1); }
.cm-s-material .cm-string { color: #C3E88D; }
.cm-s-material .cm-string-2 { color: #80CBC4; }
.cm-s-material .cm-comment { color: #546E7A; }
.cm-s-material .cm-variable { color: #82B1FF; }
.cm-s-material .cm-tag { color: #80CBC4; }
.cm-s-material .cm-meta { color: #80CBC4; }
.cm-s-material .cm-attribute { color: #FFCB6B; }
.cm-s-material .cm-property { color: #80CBAE; }
.cm-s-material .cm-qualifier { color: #DECB6B; }
.cm-s-material .cm-variable-3 { color: #DECB6B; }
.cm-s-material .cm-tag { color: rgba(255, 83, 112, 1); }
.cm-s-material .cm-error {
color: rgba(255, 255, 255, 1.0);
background-color: #EC5F67;
}
.cm-s-material .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
/* Based on Sublime Text's Monokai theme */
.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; }
.cm-s-monokai div.CodeMirror-selected { background: #49483E; }
.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; }
.cm-s-monokai .CodeMirror-guttermarker { color: white; }
.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
.cm-s-monokai span.cm-comment { color: #75715e; }
.cm-s-monokai span.cm-atom { color: #ae81ff; }
.cm-s-monokai span.cm-number { color: #ae81ff; }
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }
.cm-s-monokai span.cm-variable { color: #f8f8f2; }
.cm-s-monokai span.cm-variable-2 { color: #9effff; }
.cm-s-monokai span.cm-variable-3 { color: #66d9ef; }
.cm-s-monokai span.cm-def { color: #fd971f; }
.cm-s-monokai span.cm-bracket { color: #f8f8f2; }
.cm-s-monokai span.cm-tag { color: #f92672; }
.cm-s-monokai span.cm-header { color: #ae81ff; }
.cm-s-monokai span.cm-link { color: #ae81ff; }
.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; }
.cm-s-monokai .CodeMirror-activeline-background { background: #373831; }
.cm-s-monokai .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("wa", function(config) {
var indentUnit = config.indentUnit;
var keywords = {
"break":true, "case":true, "const":true, "continue":true,
"default":true, "defer":true, "else":true, "for":true,
"func":true, "if":true, "import":true,
"interface":true, "map":true, "range":true, "return":true,
"struct":true, "switch":true, "type":true, "global":true,
"bool":true, "byte":true, "complex64":true, "complex128":true,
"f32":true, "f64":true, "i32":true, "i64":true, "u8":true, "u16":true, "u32":true, "u64": true,
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
"uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
"rune":true
};
var atoms = {
"true":true, "false":true, "iota":true, "nil":true, "append":true,
"cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
"len":true, "make":true, "new":true, "panic":true, "print":true,
"println":true, "real":true, "assert":true, "this": true
};
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
var curPunc;
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'" || ch == "`") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\d\.]/.test(ch)) {
if (ch == ".") {
stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
} else if (ch == "0") {
stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
} else {
stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
}
return "number";
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (ch == "#") {
stream.skipToEnd();
return "comment";
}
if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
}
if (stream.eat("/")) {
stream.skipToEnd();
return "comment";
}
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";
}
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (cur == "case" || cur == "default") curPunc = "case";
return "keyword";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
escaped = !escaped && quote != "`" && next == "\\";
}
if (end || !(escaped || quote == "`"))
state.tokenize = tokenBase;
return "string";
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context);
}
function popContext(state) {
if (!state.context.prev) return;
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
return state.context = state.context.prev;
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function(stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
if (ctx.type == "case") ctx.type = "}";
}
if (stream.eatSpace()) return null;
curPunc = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment") return style;
if (ctx.align == null) ctx.align = true;
if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "case") ctx.type = "case";
else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
else if (curPunc == ctx.type) popContext(state);
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
state.context.type = "}";
return ctx.indented;
}
var closing = firstChar == ctx.type;
if (ctx.align) return ctx.column + (closing ? 0 : 1);
else return ctx.indented + (closing ? 0 : indentUnit);
},
electricChars: "{}):",
closeBrackets: "()[]{}''\"\"``",
fold: "brace",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});
CodeMirror.defineMIME("text/x-wa", "wa");
});
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-rtl pre { direction: rtl; }
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
此差异已折叠。
/**
* Adapted from jQuery Lined Textarea Plugin
* http://alan.blog-city.com/jquerylinedtextarea.htm
*
* Released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*/
(function($) {
$.fn.linedtextarea = function() {
/*
* Helper function to make sure the line numbers are always kept up to
* the current system
*/
var fillOutLines = function(linesDiv, h, lineNo) {
while (linesDiv.height() <= h) {
linesDiv.append("<div>" + lineNo + "</div>");
lineNo++;
}
return lineNo;
};
return this.each(function() {
var lineNo = 1;
var textarea = $(this);
/* Wrap the text area in the elements we need */
textarea.wrap("<div class='linedtextarea' style='height:100%; overflow:hidden'></div>");
textarea.width("97%");
textarea.parent().prepend("<div class='lines' style='width:3%'></div>");
var linesDiv = textarea.parent().find(".lines");
var scroll = function(tn) {
var domTextArea = $(this)[0];
var scrollTop = domTextArea.scrollTop;
var clientHeight = domTextArea.clientHeight;
linesDiv.css({
'margin-top' : (-scrollTop) + "px"
});
lineNo = fillOutLines(linesDiv, scrollTop + clientHeight,
lineNo);
};
/* React to the scroll event */
textarea.scroll(scroll);
$(window).resize(function() { textarea.scroll(); });
/* We call scroll once to add the line numbers */
textarea.scroll();
});
};
})(jQuery);
\ No newline at end of file
html {
height: 100%;
}
body {
color: #fff;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: #222;
}
a {
color: #1CA8B2;
}
#wrap,
#about {
padding: 5px;
margin: 0;
position: absolute;
top: 50px;
bottom: 25%;
left: 0;
right: 0;
}
#wrap {
background-color: #263238;
}
#about {
display: none;
z-index: 100;
padding: 10px 40px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
overflow: auto;
}
#about p {
max-width: 520px;
}
#about ul {
max-width: 480px;
}
#about li {
margin-bottom: 1em;
}
#code, #output, pre, .lines {
font-family: 'Go', 'Inconsolata', monospace;
font-size: 11pt;
}
#code {
color: black;
background: inherit;
width: 100%;
height: 100%;
padding: 0; margin: 0;
border: none;
outline: none;
resize: none;
wrap: off;
float: right;
background-color: #222;
color: #fff;
}
#output {
position: absolute;
top: 75%;
bottom: 0;
left: 0;
right: 0;
padding: 8px;
}
#output .system, #output .loading {
color: #555;
}
#output .stderr, #output .error {
color: #900;
}
#output pre {
margin: 0;
}
#banner {
position: absolute;
left: 0;
right: 0;
top: 0;
height: 50px;
background-color: #222;
color: #efefef;
}
#head {
float: left;
padding: 15px 10px;
font-size: 20px;
font-family: Helvetica, Arial, sans-serif;
}
#controls {
float: left;
padding: 10px 15px;
min-width: 245px;
}
#run {
background-color: #1CA8B2;
color: #ddd;
}
#run:hover {
color: #fff;
}
#about {
background-color: #333;
}
#aboutControls {
float: right;
padding: 10px 15px;
}
input[type=button],
#importsBox {
height: 30px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
color: #fff;
position: static;
top: 1px;
}
input[type=button] {
background: #333;
border: 1px solid #222;
cursor: pointer;
color: #bbb;
border-radius: 4px;
}
input[type=button]:hover {
color: #fff;
border: 1px solid #eee;
}
#importsBox {
position: relative;
display: inline;
padding: 5px 0;
margin-right: 5px;
}
#importsBox input {
position: relative;
top: -2px;
left: 1px;
height: 10px;
width: 10px;
}
#shareURL {
width: 280px;
font-size: 12px;
border: 1px solid #333;
background: #555;
color: #eee;
height: 23px;
}
#embedLabel {
font-family: Helvetica, Arial, sans-serif;
}
.lines {
float: left;
overflow: hidden;
text-align: right;
}
.lines div {
padding-right: 5px;
color: #555;
}
.lines div:hover {
color: #aaa;
background-color: #222;
}
.lines div.lineerror {
background-color: #222;
color: #900;
}
.exit {
color: #555;
}
#wrap .CodeMirror {
height: 100%;
font-family: 'Go', 'Inconsolata', monospace;
font-size: 11pt;
}
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-rtl pre { direction: rtl; }
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
/*
Name: Base16 Default Light
Author: Chris Kempson (http://chriskempson.com)
CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
*/
.cm-s-base16-light.CodeMirror { background: #f5f5f5; color: #202020; }
.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; }
.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 0px; }
.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; }
.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; }
.cm-s-base16-light .CodeMirror-linenumber { color: #b0b0b0; }
.cm-s-base16-light .CodeMirror-cursor { border-left: 1px solid #505050; }
.cm-s-base16-light span.cm-comment { color: #8f5536; }
.cm-s-base16-light span.cm-atom { color: #aa759f; }
.cm-s-base16-light span.cm-number { color: #aa759f; }
.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; }
.cm-s-base16-light span.cm-keyword { color: #ac4142; }
.cm-s-base16-light span.cm-string { color: #f4bf75; }
.cm-s-base16-light span.cm-variable { color: #90a959; }
.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
.cm-s-base16-light span.cm-def { color: #d28445; }
.cm-s-base16-light span.cm-bracket { color: #202020; }
.cm-s-base16-light span.cm-tag { color: #ac4142; }
.cm-s-base16-light span.cm-link { color: #aa759f; }
.cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; }
.cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; }
.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
/*
Name: material
Author: Michael Kaminsky (http://github.com/mkaminsky11)
Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme)
*/
.cm-s-material.CodeMirror {
background-color: #263238;
color: rgba(233, 237, 237, 1);
}
.cm-s-material .CodeMirror-gutters {
background: #263238;
color: rgb(83,127,126);
border: none;
}
.cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { color: rgb(83,127,126); }
.cm-s-material .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
.cm-s-material div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); }
.cm-s-material.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-material .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0); }
.cm-s-material .cm-keyword { color: rgba(199, 146, 234, 1); }
.cm-s-material .cm-operator { color: rgba(233, 237, 237, 1); }
.cm-s-material .cm-variable-2 { color: #80CBC4; }
.cm-s-material .cm-variable-3 { color: #82B1FF; }
.cm-s-material .cm-builtin { color: #DECB6B; }
.cm-s-material .cm-atom { color: #F77669; }
.cm-s-material .cm-number { color: #F77669; }
.cm-s-material .cm-def { color: rgba(233, 237, 237, 1); }
.cm-s-material .cm-string { color: #C3E88D; }
.cm-s-material .cm-string-2 { color: #80CBC4; }
.cm-s-material .cm-comment { color: #546E7A; }
.cm-s-material .cm-variable { color: #82B1FF; }
.cm-s-material .cm-tag { color: #80CBC4; }
.cm-s-material .cm-meta { color: #80CBC4; }
.cm-s-material .cm-attribute { color: #FFCB6B; }
.cm-s-material .cm-property { color: #80CBAE; }
.cm-s-material .cm-qualifier { color: #DECB6B; }
.cm-s-material .cm-variable-3 { color: #DECB6B; }
.cm-s-material .cm-tag { color: rgba(255, 83, 112, 1); }
.cm-s-material .cm-error {
color: rgba(255, 255, 255, 1.0);
background-color: #EC5F67;
}
.cm-s-material .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
/* Based on Sublime Text's Monokai theme */
.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; }
.cm-s-monokai div.CodeMirror-selected { background: #49483E; }
.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; }
.cm-s-monokai .CodeMirror-guttermarker { color: white; }
.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
.cm-s-monokai span.cm-comment { color: #75715e; }
.cm-s-monokai span.cm-atom { color: #ae81ff; }
.cm-s-monokai span.cm-number { color: #ae81ff; }
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }
.cm-s-monokai span.cm-variable { color: #f8f8f2; }
.cm-s-monokai span.cm-variable-2 { color: #9effff; }
.cm-s-monokai span.cm-variable-3 { color: #66d9ef; }
.cm-s-monokai span.cm-def { color: #fd971f; }
.cm-s-monokai span.cm-bracket { color: #f8f8f2; }
.cm-s-monokai span.cm-tag { color: #f92672; }
.cm-s-monokai span.cm-header { color: #ae81ff; }
.cm-s-monokai span.cm-link { color: #ae81ff; }
.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; }
.cm-s-monokai .CodeMirror-activeline-background { background: #373831; }
.cm-s-monokai .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
因为 它太大了无法显示 source diff 。你可以改为 查看blob
// Copyright 2023 The Wa Authors. All rights reserved.
function HTTPTransport() {
'use strict';
function playback(output, events) {
var timeout;
output({Kind: 'start'});
function next() {
if (!events || events.length === 0) {
output({Kind: 'end'});
return;
}
var e = events.shift();
if (e.Delay === 0) {
output({Kind: 'stdout', Body: e.Message});
next();
return;
}
timeout = setTimeout(function() {
output({Kind: 'stdout', Body: e.Message});
next();
}, e.Delay / 1000000);
}
next();
return {
Stop: function() {
clearTimeout(timeout);
}
}
}
function error(output, msg) {
output({Kind: 'start'});
output({Kind: 'stderr', Body: msg});
output({Kind: 'end'});
}
var seq = 0;
return {
Run: function(body, output, options) {
seq++;
var cur = seq;
var playing;
$.ajax(playgroundOptions.compileURL, {
type: 'POST',
data: {'version': 2, 'body': body},
dataType: 'json',
success: function(data) {
if (seq != cur) return;
if (!data) return;
if (playing != null) playing.Stop();
if (data.Errors) {
error(output, data.Errors);
return;
}
playing = playback(output, data.Events);
},
error: function() {
error(output, 'Error communicating with remote server.');
}
});
return {
Kill: function() {
if (playing != null) playing.Stop();
output({Kind: 'end', Body: 'killed'});
}
};
}
};
}
function SocketTransport() {
'use strict';
var id = 0;
var outputs = {};
var started = {};
var websocket = new WebSocket('ws://' + window.location.host + '/socket');
websocket.onclose = function() {
console.log('websocket connection closed');
}
websocket.onmessage = function(e) {
var m = JSON.parse(e.data);
var output = outputs[m.Id];
if (output === null)
return;
if (!started[m.Id]) {
output({Kind: 'start'});
started[m.Id] = true;
}
output({Kind: m.Kind, Body: m.Body});
}
function send(m) {
websocket.send(JSON.stringify(m));
}
return {
Run: function(body, output, options) {
var thisID = id+'';
id++;
outputs[thisID] = output;
send({Id: thisID, Kind: 'run', Body: body, Options: options});
return {
Kill: function() {
send({Id: thisID, Kind: 'kill'});
}
};
}
};
}
function PlaygroundOutput(el) {
'use strict';
return function(write) {
if (write.Kind == 'start') {
el.innerHTML = '';
return;
}
var cl = 'system';
if (write.Kind == 'stdout' || write.Kind == 'stderr')
cl = write.Kind;
var m = write.Body;
if (write.Kind == 'end')
m = '\nProgram exited' + (m?(': '+m):'.');
if (m.indexOf('IMAGE:') === 0) {
// TODO(adg): buffer all writes before creating image
var url = 'data:image/png;base64,' + m.substr(6);
var img = document.createElement('img');
img.src = url;
el.appendChild(img);
return;
}
// ^L clears the screen.
var s = m.split('\x0c');
if (s.length > 1) {
el.innerHTML = '';
m = s.pop();
}
m = m.replace(/&/g, '&amp;');
m = m.replace(/</g, '&lt;');
m = m.replace(/>/g, '&gt;');
var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight;
var span = document.createElement('span');
span.className = cl;
span.innerHTML = m;
el.appendChild(span);
$(el).fadeIn()
if (needScroll)
el.scrollTop = el.scrollHeight - el.offsetHeight;
}
}
var playgroundOptions = {}
var defaultOptions = {
'compileURL': '/-/play/compile',
'fmtURL': '/-/play/fmt',
};
function kclPlaygroundOptions(opts) {
playgroundOptions = $.extend(defaultOptions, playgroundOptions, opts);
}
kclPlaygroundOptions({});
(function() {
function lineHighlight(error) {
var regex = /prog.wa:([0-9]+)/g;
var r = regex.exec(error);
while (r) {
$(".lines div").eq(r[1]-1).addClass("lineerror");
r = regex.exec(error);
}
}
function highlightOutput(wrappedOutput) {
return function(write) {
if (write.Body) lineHighlight(write.Body);
wrappedOutput(write);
}
}
function lineClear() {
$(".lineerror").removeClass("lineerror");
}
// opts is an object with these keys
// codeEl - code editor element
// outputEl - program output element
// runEl - run button element
// fmtEl - fmt button element (optional)
// enableHistory - enable using HTML5 history API (optional)
// transport - playground transport to use (default is HTTPTransport)
function playground(opts) {
var opts = $.extend(opts, playgroundOptions);
var code = $(opts.codeEl);
var transport = opts['transport'] || new HTTPTransport();
var running;
console.log(code);
var editorProps = {
lineNumbers: true,
indentWithTabs: false,
mode: 'wa',
smartIndent: true,
tabSize: 4,
indentUnit: 4,
};
if (typeof opts['theme'] !== 'undefined') {
editorProps.theme = opts['theme'];
}
var editor = CodeMirror.fromTextArea(code[0], editorProps);
var outdiv = $(opts.outputEl).empty().hide();
var output = $('<pre/>').appendTo(outdiv);
function body() {
return editor.getValue();
}
function setBody(text) {
editor.setValue(text);
}
function origin(href) {
return (""+href).split("/").slice(0, 3).join("/");
}
var pushedEmpty = (window.location.pathname == "/");
function inputChanged() {
if (pushedEmpty) {
return;
}
pushedEmpty = true;
$(opts.shareURLEl).hide();
window.history.pushState(null, "", "/");
}
function popState(e) {
if (e === null) {
return;
}
if (e && e.state && e.state.code) {
setBody(e.state.code);
}
}
var rewriteHistory = false;
if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
rewriteHistory = true;
code[0].addEventListener('input', inputChanged);
window.addEventListener('popstate', popState);
}
function setError(error) {
if (running) running.Kill();
lineClear();
lineHighlight(error);
output.empty().addClass("error").text(error);
if (error === "") {
outdiv.hide();
} else {
outdiv.fadeIn();
}
}
function loading() {
lineClear();
if (running) running.Kill();
output.removeClass("error").fadeIn().text('Waiting for remote server...');
}
function run() {
$(opts.outputEl).fadeIn();
loading();
running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
}
function fmt() {
loading();
var data = {"body": body()};
data["imports"] = "true";
$.ajax(playgroundOptions.fmtURL, {
data: data,
type: "POST",
dataType: "json",
success: function(data) {
if (data.Error) {
setError(data.Error);
} else {
setBody(data.Body);
setError("");
}
}
});
}
$(opts.runEl).click(run);
$(opts.fmtEl).click(fmt);
}
window.playground = playground;
})();
html {
height: 100%;
}
body {
color: #fff;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: #222;
}
a {
color: #1CA8B2;
}
#wrap,
#about {
padding: 5px;
margin: 0;
position: absolute;
top: 50px;
bottom: 25%;
left: 0;
right: 0;
}
#wrap {
background-color: #263238;
}
#about {
display: none;
z-index: 100;
padding: 10px 40px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
overflow: auto;
}
#about p {
max-width: 520px;
}
#about ul {
max-width: 480px;
}
#about li {
margin-bottom: 1em;
}
#code, #output, pre, .lines {
font-family: 'Go', 'Inconsolata', monospace;
font-size: 11pt;
}
#code {
color: black;
background: inherit;
width: 100%;
height: 100%;
padding: 0; margin: 0;
border: none;
outline: none;
resize: none;
wrap: off;
float: right;
background-color: #222;
color: #fff;
}
#output {
position: absolute;
top: 75%;
bottom: 0;
left: 0;
right: 0;
padding: 8px;
}
#output .system, #output .loading {
color: #555;
}
#output .stderr, #output .error {
color: #900;
}
#output pre {
margin: 0;
}
#banner {
position: absolute;
left: 0;
right: 0;
top: 0;
height: 50px;
background-color: #222;
color: #efefef;
}
#head {
float: left;
padding: 15px 10px;
font-size: 20px;
font-family: Helvetica, Arial, sans-serif;
}
#controls {
float: left;
padding: 10px 15px;
min-width: 245px;
}
#run {
background-color: #1CA8B2;
color: #ddd;
}
#run:hover {
color: #fff;
}
#about {
background-color: #333;
}
#aboutControls {
float: right;
padding: 10px 15px;
}
input[type=button],
#importsBox {
height: 30px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
color: #fff;
position: static;
top: 1px;
}
input[type=button] {
background: #333;
border: 1px solid #222;
cursor: pointer;
color: #bbb;
border-radius: 4px;
}
input[type=button]:hover {
color: #fff;
border: 1px solid #eee;
}
#importsBox {
position: relative;
display: inline;
padding: 5px 0;
margin-right: 5px;
}
#importsBox input {
position: relative;
top: -2px;
left: 1px;
height: 10px;
width: 10px;
}
#shareURL {
width: 280px;
font-size: 12px;
border: 1px solid #333;
background: #555;
color: #eee;
height: 23px;
}
#embedLabel {
font-family: Helvetica, Arial, sans-serif;
}
.lines {
float: left;
overflow: hidden;
text-align: right;
}
.lines div {
padding-right: 5px;
color: #555;
}
.lines div:hover {
color: #aaa;
background-color: #222;
}
.lines div.lineerror {
background-color: #222;
color: #900;
}
.exit {
color: #555;
}
#wrap .CodeMirror {
height: 100%;
font-family: 'Go', 'Inconsolata', monospace;
font-size: 11pt;
}
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
"fmt"
"os/exec"
"runtime"
"strings"
"time"
)
func RunPlayground(addr string) error {
if strings.HasPrefix(addr, ":") {
addr = "localhost" + addr
}
fmt.Printf("listen at http://%s\n", addr)
go func() {
time.Sleep(time.Second * 2)
openBrowser(addr)
}()
s := NewWebServer()
return s.Run(addr)
}
func openBrowser(url string) error {
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
switch runtime.GOOS {
case "linux":
return exec.Command("xdg-open", url).Start()
case "windows":
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
return exec.Command("open", url).Start()
default:
return fmt.Errorf("unsupported platform")
}
}
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
"bytes"
"embed"
"fmt"
"io/fs"
"net/http"
"strings"
"time"
)
type WebServer struct {
fs fs.FS
}
//go:embed _static
var fs_static embed.FS
func getStaticFS() fs.FS {
if true {
// return fs_static
}
fs, err := fs.Sub(fs_static, "_static")
if err != nil {
panic(err)
}
return fs
}
func NewWebServer() *WebServer {
p := &WebServer{
fs: getStaticFS(),
}
return p
}
func (p *WebServer) Run(addr string) error {
startTime := time.Now()
return http.ListenAndServe(addr,
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Method, r.URL.Path)
switch {
case r.URL.Path == "/":
p.editHandler(w, r)
case r.URL.Path == "/-/play/compile":
p.runHandler(w, r)
case r.URL.Path == "/-/play/fmt":
p.fmtHandler(w, r)
case strings.HasPrefix(r.URL.Path, "/static/"):
relpath := strings.TrimPrefix(r.URL.Path, "/static/")
data, err := fs.ReadFile(p.fs, relpath)
if err != nil {
http.NotFound(w, r)
return
}
http.ServeContent(w, r, r.URL.Path, startTime, bytes.NewReader(data))
default:
p.editHandler(w, r)
}
}),
)
}
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
_ "embed"
"html/template"
"net/http"
)
func (p *WebServer) editHandler(w http.ResponseWriter, r *http.Request) {
snip := &Snippet{Body: []byte(edit_helloPlayground)}
edit_Template.Execute(w, &editData{snip})
}
//go:embed _edit.tmpl.html
var edit_tmpl string
var edit_Template = template.Must(template.New("playground/index.html").Parse(edit_tmpl))
const edit_helloPlayground = `// 版权 @2019 凹语言 作者。保留所有权利。
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
}
`
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
"encoding/json"
"fmt"
"net/http"
"wa-lang.org/wa/api"
)
func (p *WebServer) fmtHandler(w http.ResponseWriter, r *http.Request) {
var (
in = []byte(r.FormValue("body"))
err error
)
resp, err := p.fmtCode(in)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError)
return
}
}
func (p *WebServer) fmtCode(code []byte) (*fmtResponse, error) {
output, err := api.FormatCode("prog.wa", string(code))
if err != nil {
resp := &fmtResponse{
Error: err.Error(),
}
return resp, nil
}
resp := &fmtResponse{
Body: string(output),
}
return resp, nil
}
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"wa-lang.org/wa/api"
)
func (p *WebServer) runHandler(w http.ResponseWriter, r *http.Request) {
var req Request
version := r.PostFormValue("version")
if version == "2" {
req.Body = r.PostFormValue("body")
} else {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("error decoding request: %v", err), http.StatusBadRequest)
return
}
}
resp, err := p.compileAndRun(&req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError)
return
}
}
func (p *WebServer) compileAndRun(req *Request) (*Response, error) {
tmpDir, err := ioutil.TempDir("", "sandbox")
if err != nil {
return nil, fmt.Errorf("error creating temp directory: %v", err)
}
defer os.RemoveAll(tmpDir)
result, err := api.RunCode(api.DefaultConfig(), "prog.wa", req.Body)
if err != nil {
resp := &Response{Errors: err.Error()}
return resp, nil
}
resp := &Response{
Events: []Event{
{
Message: string(result),
Kind: "stdout",
},
},
}
return resp, nil
}
// 版权 @2023 凹语言 作者。保留所有权利。
package appplay
import (
"time"
)
type Snippet struct {
Body []byte
}
type Request struct {
Body string
}
type Response struct {
Errors string
Events []Event
}
type Event struct {
Message string
Kind string // "stdout" or "stderr"
Delay time.Duration // time to wait before printing Message
}
type editData struct {
Snippet *Snippet
}
type fmtResponse struct {
Body string
Error string
}
// 版权 @2023 凹语言 作者。保留所有权利。
package app
package apptest
import (
"bytes"
"context"
"crypto/rand"
"fmt"
"os"
"strings"
"time"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/sys"
"wa-lang.org/wa/internal/app/apputil"
"wa-lang.org/wa/internal/app/waruntime"
"wa-lang.org/wa/internal/backends/compiler_wat"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/loader"
"wa-lang.org/wa/internal/wabt"
"wa-lang.org/wa/internal/wazero"
)
func (p *App) RunTest(pkgpath string, appArgs ...string) error {
cfg := p.opt.Config()
func RunTest(cfg *config.Config, pkgpath string, appArgs ...string) {
cfg.UnitTest = true
prog, err := loader.LoadProgram(cfg, pkgpath)
if err != nil {
return err
fmt.Println(err)
os.Exit(1)
}
startTime := time.Now()
mainPkg := prog.Pkgs[prog.Manifest.MainPkg]
wasmName := "unittest://" + pkgpath
wasmArgs := []string{}
if len(mainPkg.TestInfo.Files) == 0 {
fmt.Printf("? %s [no test files]\n", prog.Manifest.MainPkg)
return nil
return
}
// 生成 wat 文件(main 函数为空)
watOutput, err := compiler_wat.New().Compile(prog, "")
if err != nil {
return err
fmt.Println(err)
os.Exit(1)
}
// 编译为 wasm
if err = os.WriteFile("a.out.wat", []byte(watOutput), 0666); err != nil {
return err
}
wasmBytes, stderr, err := apputil.RunWat2Wasm("a.out.wat")
if err != nil {
if s := sWithPrefix(string(stderr), " "); s != "" {
fmt.Println(s)
}
return err
}
// os.WriteFile("a.out.wasm", wasmBytes, 0666)
// 构建 wasm 可执行实例
// https://pkg.go.dev/github.com/tetratelabs/wazero@v1.0.0-pre.4#section-readme
var r wazero.Runtime
var ctx = context.Background()
var stdoutBuffer = new(bytes.Buffer)
var stderrBuffer = new(bytes.Buffer)
{
r = wazero.NewRuntime(ctx)
defer r.Close(ctx)
switch cfg.WaOS {
case config.WaOS_arduino:
if _, err = waruntime.ArduinoInstantiate(ctx, r); err != nil {
if s := sWithPrefix(stderrBuffer.String(), " "); s != "" {
fmt.Println(s)
}
return err
}
case config.WaOS_chrome:
if _, err = waruntime.ChromeInstantiate(ctx, r); err != nil {
if s := sWithPrefix(stderrBuffer.String(), " "); s != "" {
fmt.Println(s)
}
return err
}
case config.WaOS_wasi:
if _, err = waruntime.WasiInstantiate(ctx, r); err != nil {
if s := sWithPrefix(stderrBuffer.String(), " "); s != "" {
fmt.Println(s)
}
return err
}
}
}
// wat 落盘(仅用于调试)
os.WriteFile("a.out.wat", []byte(watOutput), 0666)
conf := wazero.NewModuleConfig().
WithStdout(stdoutBuffer).
WithStderr(stderrBuffer).
WithStdin(os.Stdin).
WithRandSource(rand.Reader).
WithSysNanosleep().
WithSysNanotime().
WithSysWalltime().
WithArgs("a.out.wasm").
WithName("unittest")
// 执行 init 函数
compiled, err := r.CompileModule(ctx, wasmBytes)
// 编译为 wasm
wasmBytes, err := wabt.Wat2Wasm([]byte(watOutput))
if err != nil {
return err
fmt.Println(err)
os.Exit(1)
}
wasmIns, err := r.InstantiateModule(ctx, compiled, conf)
m, err := wazero.BuildModule(cfg, wasmName, wasmBytes, wasmArgs...)
if err != nil {
if s := sWithPrefix(stderrBuffer.String(), " "); s != "" {
fmt.Println(s)
}
return err
fmt.Println(err)
os.Exit(1)
}
defer m.Close()
// 执行测试函数
var firstError error
for _, t := range mainPkg.TestInfo.Tests {
stdoutBuffer.Reset()
stderrBuffer.Reset()
_, err := wasmIns.ExportedFunction(mainPkg.Pkg.Path() + "." + t.Name).Call(ctx)
_, stdout, stderr, err := m.RunFunc(mainPkg.Pkg.Path() + "." + t.Name)
if err != nil {
if s := sWithPrefix(stderrBuffer.String(), " "); s != "" {
fmt.Println(s)
if len(stdout) > 0 {
if s := sWithPrefix(string(stdout), " "); s != "" {
fmt.Println(s)
}
}
return err
if len(stderr) > 0 {
if s := sWithPrefix(string(stderr), " "); s != "" {
fmt.Println(s)
}
}
os.Exit(1)
}
stdout := stdoutBuffer.Bytes()
stderr := stderrBuffer.Bytes()
stdout = bytes.TrimSpace(stdout)
if t.Output != "" && t.Output == string(stdout) {
continue
......@@ -142,7 +85,7 @@ func (p *App) RunTest(pkgpath string, appArgs ...string) error {
if firstError == nil {
firstError = err
}
if _, ok := err.(*sys.ExitError); ok {
if _, ok := wazero.AsExitError(err); ok {
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
if s := sWithPrefix(string(stdout), " "); s != "" {
fmt.Println(s)
......@@ -166,21 +109,24 @@ func (p *App) RunTest(pkgpath string, appArgs ...string) error {
}
}
for _, t := range mainPkg.TestInfo.Examples {
output, err := compiler_wat.New().Compile(prog, t.Name)
_, stdout, stderr, err := m.RunFunc(mainPkg.Pkg.Path() + "." + t.Name)
if err != nil {
return err
}
if len(stdout) > 0 {
if s := sWithPrefix(string(stdout), " "); s != "" {
fmt.Println(s)
}
}
if len(stderr) > 0 {
if s := sWithPrefix(string(stderr), " "); s != "" {
fmt.Println(s)
}
}
if err = os.WriteFile("a.out.wat", []byte(output), 0666); err != nil {
return err
os.Exit(1)
}
stdout, stderr, err := apputil.RunWasmEx(cfg, "a.out.wat", appArgs...)
stdout = bytes.TrimSpace(stdout)
bOutputOK := t.Output == string(stdout)
if err == nil && bOutputOK {
if t.Output != "" && t.Output == string(stdout) {
continue
}
......@@ -188,7 +134,7 @@ func (p *App) RunTest(pkgpath string, appArgs ...string) error {
if firstError == nil {
firstError = err
}
if _, ok := err.(*sys.ExitError); ok {
if _, ok := wazero.AsExitError(err); ok {
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
if s := sWithPrefix(string(stdout), " "); s != "" {
fmt.Println(s)
......@@ -218,7 +164,7 @@ func (p *App) RunTest(pkgpath string, appArgs ...string) error {
fmt.Printf("ok %s %v\n", prog.Manifest.MainPkg, time.Now().Sub(startTime).Round(time.Millisecond))
return nil
return
}
func sWithPrefix(s, prefix string) string {
......
// 版权 @2019 凹语言 作者。保留所有权利。
package apputil
import (
"bytes"
"context"
"crypto/rand"
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/sys"
"wa-lang.org/wa/internal/app/waruntime"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wabt-go"
wabt_wasm "wa-lang.org/wabt-go/wabt-wasm"
)
func getWatAbsDir(filename string) string {
dir := filepath.Dir(filename)
abs, err := filepath.Abs(dir)
if err != nil {
return filepath.ToSlash(dir)
}
return filepath.ToSlash(abs)
}
func RunWasm(cfg *config.Config, filename string, wasmArgs ...string) (stdoutStderr []byte, err error) {
stdout, stderr, err := runWasm(cfg, filename, wasmArgs...)
stdoutStderr = append(stdout, stderr...)
return
}
func RunWasmEx(cfg *config.Config, filename string, wasmArgs ...string) (stdout, stderr []byte, err error) {
stdout, stderr, err = runWasm(cfg, filename, wasmArgs...)
return
}
func RunWat2Wasm(filename string) (stdout, stderr []byte, err error) {
watDir := getWatAbsDir(filename)
stdout, stderr, err = xRunWat2Wasm_exe(watDir, filename, "--output=-")
return
}
func runWasm(cfg *config.Config, filename string, wasmArgs ...string) (stdout, stderr []byte, err error) {
dst := filename
if strings.HasSuffix(filename, ".wat") {
dst = filename + ".wasm"
defer func() {
if err == nil {
os.Remove(dst)
}
}()
if stdout, stderr, err = xRunWat2Wasm_exe("", filename, "--output=-"); err != nil {
return stdout, stderr, err
}
os.WriteFile(dst, stdout, 0666)
}
wasmBytes, err := os.ReadFile(dst)
if err != nil {
return nil, nil, err
}
wasmExe := filepath.Base(filename)
stdoutBuffer := new(bytes.Buffer)
stderrBuffer := new(bytes.Buffer)
conf := wazero.NewModuleConfig().
WithStdout(stdoutBuffer).
WithStderr(stderrBuffer).
WithStdin(os.Stdin).
WithRandSource(rand.Reader).
WithSysNanosleep().
WithSysNanotime().
WithSysWalltime().
WithArgs(append([]string{wasmExe}, wasmArgs...)...)
// TODO: Windows 可能导致异常, 临时屏蔽
if runtime.GOOS != "windows" {
for _, s := range os.Environ() {
var key, value string
if kv := strings.Split(s, "="); len(kv) >= 2 {
key = kv[0]
value = kv[1]
} else if len(kv) >= 1 {
key = kv[0]
}
conf = conf.WithEnv(key, value)
}
}
ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx)
code, err := r.CompileModule(ctx, wasmBytes)
if err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
switch cfg.WaOS {
case config.WaOS_arduino:
if _, err = waruntime.ArduinoInstantiate(ctx, r); err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
case config.WaOS_chrome:
if _, err = waruntime.ChromeInstantiate(ctx, r); err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
case config.WaOS_wasi:
if _, err = waruntime.WasiInstantiate(ctx, r); err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
}
_, err = r.InstantiateModule(ctx, code, conf)
if err != nil {
if exitErr, ok := err.(*sys.ExitError); ok {
if exitErr.ExitCode() == 0 {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), nil
}
}
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), nil
}
func xRunWat2Wasm_wasm(dir string, args ...string) (stdout, stderr []byte, err error) {
if true {
panic("disabled")
}
stdoutBuffer := new(bytes.Buffer)
stderrBuffer := new(bytes.Buffer)
conf := wazero.NewModuleConfig().
WithFS(os.DirFS(dir)).
WithStdout(stdoutBuffer).
WithStderr(stderrBuffer).
WithStdin(os.Stdin).
WithRandSource(rand.Reader).
WithSysNanosleep().
WithSysNanotime().
WithSysWalltime().
WithArgs(append([]string{"wat2wasm.wasm"}, args...)...)
ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx)
code, err := r.CompileModule(ctx, []byte(wabt_wasm.LoadWat2Wasm()))
if err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
if _, err = waruntime.WasiInstantiate(ctx, r); err != nil {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
_, err = r.InstantiateModule(ctx, code, conf)
if err != nil {
if exitErr, ok := err.(*sys.ExitError); ok {
if exitErr.ExitCode() == 0 {
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), nil
}
}
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), nil
}
var muRunWat2Wasm sync.Mutex
var wat2wasmPath string
func xRunWat2Wasm_exe(_ string, args ...string) (stdout, stderr []byte, err error) {
muRunWat2Wasm.Lock()
defer muRunWat2Wasm.Unlock()
if wat2wasmPath == "" {
logger.Tracef(&config.EnableTrace_app, "wat2wasm not found")
return nil, nil, errors.New("wat2wasm not found")
}
var bufStdout bytes.Buffer
var bufStderr bytes.Buffer
cmd := exec.Command(wat2wasmPath, args...)
cmd.Stdout = &bufStdout
cmd.Stderr = &bufStderr
err = cmd.Run()
stdout = bufStdout.Bytes()
stderr = bufStderr.Bytes()
return
}
func init() {
const baseName = "wa.wat2wasm.exe"
// 1. exe 同级目录存在 wat2wasm ?
wat2wasmPath = filepath.Join(curExeDir(), baseName)
if exeExists(wat2wasmPath) {
return
}
// 2. 当前目录存在 wat2wasm ?
cwd, _ := os.Getwd()
wat2wasmPath = filepath.Join(cwd, baseName)
if exeExists(wat2wasmPath) {
return
}
// 3. 本地系统存在 wat2wasm ?
if s, _ := exec.LookPath(baseName); s != "" {
wat2wasmPath = s
return
}
// 4. wat2wasm 安装到 exe 所在目录 ?
wat2wasmPath = filepath.Join(curExeDir(), baseName)
if err := os.WriteFile(wat2wasmPath, wabt.LoadWat2Wasm(), 0777); err != nil {
logger.Tracef(&config.EnableTrace_app, "install wat2wasm failed: %+v", err)
return
}
}
// 是否为目录
func isDir(path string) bool {
if fi, _ := os.Lstat(path); fi != nil && fi.IsDir() {
return true
}
return false
}
// exe 文件存在
func exeExists(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
if !fi.Mode().IsRegular() {
return false
}
return true
}
// 当前执行程序所在目录
func curExeDir() string {
s, err := os.Executable()
if err != nil {
logger.Panicf("os.Executable() failed: %+v", err)
}
return filepath.Dir(s)
}
......@@ -4,7 +4,7 @@
// +build !wasm
// 凹语言,The Wa Programming Language.
package wacli
package app
import (
"fmt"
......@@ -13,15 +13,13 @@ import (
"strings"
"time"
"github.com/tetratelabs/wazero/sys"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/3rdparty/cli"
"wa-lang.org/wa/internal/app"
"wa-lang.org/wa/internal/app/apputil"
"wa-lang.org/wa/internal/app/yacc"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/lsp"
"wa-lang.org/wa/internal/wabt"
"wa-lang.org/wa/internal/wazero"
)
func Main() {
......@@ -128,7 +126,7 @@ func Main() {
},
Action: func(c *cli.Context) error {
waApp := app.NewApp(build_Options(c))
waApp := NewApp(build_Options(c))
err := waApp.InitApp(c.String("name"), c.String("pkgpath"), c.Bool("update"))
if err != nil {
fmt.Println(err)
......@@ -194,8 +192,8 @@ func Main() {
input, _ = os.Getwd()
}
ctx := app.NewApp(build_Options(c))
output, err := ctx.WASM(input)
ctx := NewApp(build_Options(c))
watOutput, err := ctx.WASM(input)
if err != nil {
fmt.Println(err)
os.Exit(1)
......@@ -206,28 +204,25 @@ func Main() {
if !strings.HasSuffix(watFilename, ".wat") {
watFilename += ".wat"
}
err := os.WriteFile(watFilename, []byte(output), 0666)
err := os.WriteFile(watFilename, []byte(watOutput), 0666)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if strings.HasSuffix(outfile, ".wasm") {
stdout, stderr, err := apputil.RunWat2Wasm(watFilename)
wasmBytes, err := wabt.Wat2Wasm(watOutput)
if err != nil {
if len(stderr) != 0 {
fmt.Println(string(stderr))
}
fmt.Println(err)
os.Exit(1)
}
if err := os.WriteFile(outfile, stdout, 0666); err != nil {
if err := os.WriteFile(outfile, wasmBytes, 0666); err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Remove(watFilename)
}
} else {
fmt.Println(string(output))
fmt.Println(string(watOutput))
}
return nil
......@@ -278,7 +273,7 @@ func Main() {
}
infile = c.Args().First()
ctx := app.NewApp(build_Options(c, config.WaBackend_llvm))
ctx := NewApp(build_Options(c, config.WaBackend_llvm))
if err := ctx.LLVM(infile, outfile, target, debug); err != nil {
fmt.Println(err)
os.Exit(1)
......@@ -298,7 +293,7 @@ func Main() {
os.Exit(1)
}
waApp := app.NewApp(build_Options(c))
waApp := NewApp(build_Options(c))
err := waApp.Lex(c.Args().First())
if err != nil {
fmt.Println(err)
......@@ -317,7 +312,7 @@ func Main() {
os.Exit(1)
}
waApp := app.NewApp(build_Options(c))
waApp := NewApp(build_Options(c))
err := waApp.AST(c.Args().First())
if err != nil {
fmt.Println(err)
......@@ -336,7 +331,7 @@ func Main() {
os.Exit(1)
}
ctx := app.NewApp(build_Options(c))
ctx := NewApp(build_Options(c))
err := ctx.SSA(c.Args().First())
if err != nil {
fmt.Println(err)
......@@ -355,7 +350,7 @@ func Main() {
os.Exit(1)
}
ctx := app.NewApp(build_Options(c))
ctx := NewApp(build_Options(c))
err := ctx.CIR(c.Args().First())
if err != nil {
fmt.Println(err)
......@@ -373,8 +368,17 @@ func Main() {
cli.ShowAppHelpAndExit(c, 0)
}
appArgs := c.Args().Slice()[1:]
waApp := app.NewApp(build_Options(c))
err := waApp.RunTest(c.Args().First(), appArgs...)
waApp := NewApp(build_Options(c))
waApp.RunTest(c.Args().First(), appArgs...)
return nil
},
},
{
Name: "fmt",
Usage: "format Wa package sources",
Action: func(c *cli.Context) error {
waApp := NewApp(build_Options(c))
err := waApp.Fmt(c.Args().First())
if err != nil {
fmt.Println(err)
os.Exit(1)
......@@ -382,12 +386,20 @@ func Main() {
return nil
},
},
{
Name: "fmt",
Usage: "format Wa package sources",
Name: "play",
Usage: "run wa playground server",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "http",
Value: ":2023",
Usage: "set http address",
},
},
Action: func(c *cli.Context) error {
waApp := app.NewApp(build_Options(c))
err := waApp.Fmt(c.Args().First())
waApp := NewApp(build_Options(c))
err := waApp.Playground(c.String("http"))
if err != nil {
fmt.Println(err)
os.Exit(1)
......@@ -477,7 +489,7 @@ func Main() {
},
},
Action: func(c *cli.Context) error {
app.PrintLogo(c.Bool("more"))
PrintLogo(c.Bool("more"))
return nil
},
},
......@@ -486,8 +498,8 @@ func Main() {
cliApp.Run(os.Args)
}
func build_Options(c *cli.Context, waBackend ...string) *app.Option {
opt := &app.Option{
func build_Options(c *cli.Context, waBackend ...string) *Option {
opt := &Option{
Debug: c.Bool("debug"),
WaBackend: config.WaBackend_Default,
BuilgTags: strings.Fields(c.String("tags")),
......@@ -525,38 +537,42 @@ func cliRun(c *cli.Context) {
infile, _ = os.Getwd()
}
var app = app.NewApp(build_Options(c))
var outfile string
var output []byte
var app = NewApp(build_Options(c))
var watBytes []byte
var wasmBytes []byte
var err error
if !c.Bool("debug") {
defer os.Remove(outfile)
}
switch {
case strings.HasSuffix(infile, ".wat"):
outfile = infile
output, err = os.ReadFile(infile)
watBytes, err = os.ReadFile(infile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
wasmBytes, err = wabt.Wat2Wasm(watBytes)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
case strings.HasSuffix(infile, ".wasm"):
outfile = infile
output, err = os.ReadFile(infile)
wasmBytes, err = os.ReadFile(infile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
default:
outfile = "a.out.wat"
output, err = app.WASM(infile)
watBytes, err = app.WASM(infile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = os.WriteFile(outfile, []byte(output), 0666); err != nil {
if err = os.WriteFile("a.out.wat", watBytes, 0666); err != nil {
fmt.Println(err)
os.Exit(1)
}
wasmBytes, err = wabt.Wat2Wasm(watBytes)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
......@@ -567,18 +583,24 @@ func cliRun(c *cli.Context) {
appArgs = c.Args().Slice()[1:]
}
stdoutStderr, err := apputil.RunWasm(app.GetConfig(), outfile, appArgs...)
stdout, stderr, err := wazero.RunWasm(app.GetConfig(), infile, wasmBytes, appArgs...)
if err != nil {
if len(stdoutStderr) > 0 {
fmt.Println(string(stdoutStderr))
if len(stdout) > 0 {
fmt.Fprint(os.Stdout, string(stdout))
}
if len(stderr) > 0 {
fmt.Fprint(os.Stderr, string(stderr))
}
if exitErr, ok := err.(*sys.ExitError); ok {
os.Exit(int(exitErr.ExitCode()))
if exitCode, ok := wazero.AsExitError(err); ok {
os.Exit(exitCode)
}
fmt.Println(err)
}
if len(stdoutStderr) > 0 {
fmt.Println(string(stdoutStderr))
if len(stdout) > 0 {
fmt.Fprint(os.Stdout, string(stdout))
}
if len(stderr) > 0 {
fmt.Fprint(os.Stderr, string(stderr))
}
}
......
......@@ -725,7 +725,7 @@ outer:
// put out names of tokens
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %sToknames = [...]string{\n", prefix)
fmt.Fprintf(ftable, "global %sToknames = [...]string{\n", prefix)
for i := 1; i <= ntokens; i++ {
fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name)
}
......@@ -735,7 +735,7 @@ outer:
// commented out to avoid a huge table just for debugging.
// re-enable to have the names in the binary.
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %sStatenames = [...]string{\n", prefix)
fmt.Fprintf(ftable, "global %sStatenames = [...]string{\n", prefix)
// for i:=TOKSTART; i<=ntokens; i++ {
// fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name);
// }
......@@ -1094,7 +1094,7 @@ out:
lineno++
case '{':
if level == 0 {
fmt.Fprintf(ftable, "\n\tyys int")
fmt.Fprintf(ftable, "\n\tyys :int")
}
level++
case '}':
......@@ -2889,7 +2889,7 @@ func others() {
// table 3 has everything else
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %sTok3 = [...]int{\n\t", prefix)
fmt.Fprintf(ftable, "global %sTok3 = [...]int{\n\t", prefix)
c = 0
for i = 1; i <= ntokens; i++ {
j = tokset[i].value
......@@ -2917,12 +2917,12 @@ func others() {
// Custom error messages.
fmt.Fprintf(ftable, "\n")
fmt.Fprintf(ftable, "type %sErrorMessageInfo struct {\n", prefix)
fmt.Fprintf(ftable, "\tstate int\n")
fmt.Fprintf(ftable, "\ttoken int\n")
fmt.Fprintf(ftable, "\tmsg string\n")
fmt.Fprintf(ftable, "\tstate :int\n")
fmt.Fprintf(ftable, "\ttoken :int\n")
fmt.Fprintf(ftable, "\tmsg :string\n")
fmt.Fprintf(ftable, "}\n")
fmt.Fprintf(ftable, "var %sErrorMessages = [...]%sErrorMessageInfo {\n", prefix, prefix)
fmt.Fprintf(ftable, "global %sErrorMessages = [...]%sErrorMessageInfo {\n", prefix, prefix)
for _, error := range errors {
lineno = error.lineno
state, token := runMachine(error.tokens)
......@@ -3006,7 +3006,7 @@ Loop:
func arout(s string, v []int, n int) {
s = prefix + s
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %v = [...]int{", s)
fmt.Fprintf(ftable, "global %v = [...]int{", s)
for i := 0; i < n; i++ {
if i%10 == 0 {
fmt.Fprintf(ftable, "\n\t")
......@@ -3249,6 +3249,7 @@ func wafmt() {
return
}
} else {
// todo: fix fmt yacc output.wa
src, err = format.SourceFile(src)
if err != nil {
return
......@@ -3261,7 +3262,7 @@ var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g
var yaccpartext = `
/* parser for yacc output */
var (
global (
$$Debug = 0
$$ErrorVerbose = false
)
......@@ -3408,9 +3409,9 @@ func $$Parse($$lex: *$$Lexer) => int {
}
func $$Parser.Parse($$lex: *$$Lexer) => int {
var $$n: int
var $$VAL: $$SymType
var $$Dollar: []$$SymType
$$n: int
$$VAL: $$SymType
$$Dollar: []$$SymType
_ = $$Dollar // silence set and not used
$$S := this.stack[:]
......@@ -3424,7 +3425,7 @@ func $$Parser.Parse($$lex: *$$Lexer) => int {
const __goto_$$stack = -1
const __goto_$$newstate = -2
const __goto_$$default = -3
var __goto_x = __goto_$$stack
__goto_x := __goto_$$stack
Loop:
for { switch __goto_x {
......
......@@ -11,7 +11,7 @@ import (
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/loader"
"wa-lang.org/wa/internal/ssa"
"wa-lang.org/wa/internal/waroot"
"wa-lang.org/wa/waroot"
)
type Compiler struct {
......
......@@ -76,17 +76,17 @@ func (g *functionGenerator) getValue(i ssa.Value) valueWrap {
val, _ := constant.Uint64Val(v.Value)
return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.U8)}
case types.Int8:
val, _ := constant.Int64Val(v.Value)
return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.I8)}
//case types.Int8:
// val, _ := constant.Int64Val(v.Value)
// return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.I8)}
case types.Uint16:
val, _ := constant.Uint64Val(v.Value)
return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.U16)}
case types.Int16:
val, _ := constant.Int64Val(v.Value)
return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.I16)}
//case types.Int16:
// val, _ := constant.Int64Val(v.Value)
// return valueWrap{value: wir.NewConst(strconv.Itoa(int(val)), g.module.I16)}
case types.Uint32, types.Uintptr:
val, _ := constant.Uint64Val(v.Value)
......@@ -624,8 +624,7 @@ func (g *functionGenerator) genBuiltin(call *ssa.CallCommon) (insts []wat.Inst,
insts = append(insts, wat.NewInstCall("$runtime.waPrintRune"))
}
if avt.Equal(g.module.U8) || avt.Equal(g.module.I8) ||
avt.Equal(g.module.U16) || avt.Equal(g.module.I16) ||
if avt.Equal(g.module.U8) || avt.Equal(g.module.U16) ||
avt.Equal(g.module.I32) || avt.Equal(g.module.U32) {
insts = append(insts, av.EmitPush()...)
insts = append(insts, wat.NewInstCall("$runtime.waPrintI32"))
......
......@@ -74,14 +74,14 @@ func (tLib *typeLib) compile(from types.Type) wir.ValueType {
case types.Float64:
newType = tLib.module.F64
case types.Int8:
newType = tLib.module.I8
//case types.Int8:
// newType = tLib.module.I8
case types.Uint8:
newType = tLib.module.U8
case types.Int16:
newType = tLib.module.I16
//case types.Int16:
// newType = tLib.module.I16
case types.Uint16:
newType = tLib.module.U16
......
......@@ -71,18 +71,45 @@ func (m *Module) EmitBinOp(x, y Value, op wat.OpCode) (insts []wat.Inst, ret_typ
} else {
insts = append(insts, wat.NewInstAdd(toWatType(rtype)))
}
if rtype.Equal(m.U8) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "255"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
} else if rtype.Equal(m.U16) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "65535"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
}
ret_type = rtype
case wat.OpCodeSub:
insts = append(insts, x.EmitPush()...)
insts = append(insts, y.EmitPush()...)
insts = append(insts, wat.NewInstSub(toWatType(rtype)))
if rtype.Equal(m.U8) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "255"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
} else if rtype.Equal(m.U16) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "65535"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
}
ret_type = rtype
case wat.OpCodeMul:
insts = append(insts, x.EmitPush()...)
insts = append(insts, y.EmitPush()...)
insts = append(insts, wat.NewInstMul(toWatType(rtype)))
if rtype.Equal(m.U8) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "255"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
} else if rtype.Equal(m.U16) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "65535"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
}
ret_type = rtype
case wat.OpCodeQuo:
......@@ -158,6 +185,15 @@ func (m *Module) EmitBinOp(x, y Value, op wat.OpCode) (insts []wat.Inst, ret_typ
insts = append(insts, y.EmitPush()...)
}
insts = append(insts, wat.NewInstShl(toWatType(rtype)))
if rtype.Equal(m.U8) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "255"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
} else if rtype.Equal(m.U16) {
insts = append(insts, wat.NewInstConst(wat.I32{}, "65535"))
insts = append(insts, wat.NewInstAnd(wat.I32{}))
}
ret_type = rtype
case wat.OpCodeShr:
......@@ -468,27 +504,27 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
xt := x.Type()
switch {
case typ.Equal(m.I8):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
break
/*case typ.Equal(m.I8):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
break
case xt.Equal(m.I64), xt.Equal(m.U64):
insts = append(insts, wat.NewInstConvert_i32_wrap_i64())
case xt.Equal(m.I64), xt.Equal(m.U64):
insts = append(insts, wat.NewInstConvert_i32_wrap_i64())
case xt.Equal(m.F32):
insts = append(insts, wat.NewInstConvert_i32_trunc_f32_s())
case xt.Equal(m.F32):
insts = append(insts, wat.NewInstConvert_i32_trunc_f32_s())
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return */
case typ.Equal(m.U8):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE): //Todo: xt.Equal(m.I8), xt.Equal(m.I16)
break
case xt.Equal(m.I64), xt.Equal(m.U64):
......@@ -504,27 +540,27 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
insts = append(insts, wat.NewInstAnd(wat.I32{}))
return
case typ.Equal(m.I16):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
break
/*case typ.Equal(m.I16):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
break
case xt.Equal(m.I64), xt.Equal(m.U64):
insts = append(insts, wat.NewInstConvert_i32_wrap_i64())
case xt.Equal(m.I64), xt.Equal(m.U64):
insts = append(insts, wat.NewInstConvert_i32_wrap_i64())
case xt.Equal(m.F32):
insts = append(insts, wat.NewInstConvert_i32_trunc_f32_s())
case xt.Equal(m.F32):
insts = append(insts, wat.NewInstConvert_i32_trunc_f32_s())
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return */
case typ.Equal(m.U16):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE): //Todo:xt.Equal(m.I8), xt.Equal(m.I16)
break
case xt.Equal(m.I64), xt.Equal(m.U64):
......@@ -543,7 +579,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case typ.Equal(m.I32), typ.Equal(m.U32), typ.Equal(m.RUNE):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.U8), xt.Equal(m.I16), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE):
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.I32), xt.Equal(m.U32), xt.Equal(m.RUNE): //Todo:xt.Equal(m.I8), xt.Equal(m.I16),
break
case xt.Equal(m.I64), xt.Equal(m.U64):
......@@ -560,7 +596,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case typ.Equal(m.I64):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.I16), xt.Equal(m.I32):
case xt.Equal(m.I32): //Todo: xt.Equal(m.I8), xt.Equal(m.I16)
insts = append(insts, wat.NewInstConvert_i64_extend_i32_s())
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.U32), xt.Equal(m.RUNE):
......@@ -580,7 +616,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case typ.Equal(m.U64):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.I16), xt.Equal(m.I32):
case xt.Equal(m.I32): //Todo: xt.Equal(m.I8), xt.Equal(m.I16)
insts = append(insts, wat.NewInstConvert_i64_extend_i32_u())
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.U32), xt.Equal(m.RUNE):
......@@ -600,7 +636,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case typ.Equal(m.F32):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.I16), xt.Equal(m.I32):
case xt.Equal(m.I32): //Todo: xt.Equal(m.I8), xt.Equal(m.I16)
insts = append(insts, wat.NewInstConvert_f32_convert_i32_s())
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.U32):
......@@ -620,7 +656,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case typ.Equal(m.F64):
insts = append(insts, x.EmitPush()...)
switch {
case xt.Equal(m.I8), xt.Equal(m.I16), xt.Equal(m.I32):
case xt.Equal(m.I32): //Todo: xt.Equal(m.I8), xt.Equal(m.I16)
insts = append(insts, wat.NewInstConvert_f64_convert_i32_s())
case xt.Equal(m.U8), xt.Equal(m.U16), xt.Equal(m.U32):
......
......@@ -18,7 +18,7 @@ type fnSigWrap struct {
Module:
**************************************/
type Module struct {
VOID, RUNE, I8, U8, I16, U16, I32, U32, UPTR, I64, U64, F32, F64, STRING, BYTES ValueType
VOID, RUNE, U8, U16, I32, U32, UPTR, I64, U64, F32, F64, STRING, BYTES ValueType
types_map map[string]ValueType
usedConcreteTypes []ValueType
......@@ -54,9 +54,9 @@ func NewModule() *Module {
m.VOID = &tVoid{}
m.RUNE = &tRune{}
m.I8 = &tI8{}
//m.I8 = &tI8{}
m.U8 = &tU8{}
m.I16 = &tI16{}
//m.I16 = &tI16{}
m.U16 = &tU16{}
m.I32 = &tI32{}
m.U32 = &tU32{}
......@@ -69,9 +69,9 @@ func NewModule() *Module {
m.types_map = make(map[string]ValueType)
m.addValueType(m.VOID)
m.addValueType(m.RUNE)
m.addValueType(m.I8)
//m.addValueType(m.I8)
m.addValueType(m.U8)
m.addValueType(m.I16)
//m.addValueType(m.I16)
m.addValueType(m.U16)
m.addValueType(m.I32)
m.addValueType(m.U32)
......
......@@ -38,7 +38,7 @@ const (
func toWatType(t ValueType) wat.ValueType {
switch t.(type) {
case *tI32, *tRune, *tI8, *tI16:
case *tI32, *tRune: //Todo: *tI8, *tI16*
return wat.I32{}
case *tU32, *tU8, *tU16:
......
......@@ -20,7 +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/waroot"
"wa-lang.org/wa/waroot"
)
type _Loader struct {
......
......@@ -13,7 +13,7 @@ import (
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wa/internal/waroot"
"wa-lang.org/wa/waroot"
)
// 根据路径加载需要的 vfs 和 manifest
......
......@@ -497,9 +497,10 @@ var stmtStart = map[token.Token]bool{
}
var declStart = map[token.Token]bool{
token.CONST: true,
token.TYPE: true,
token.VAR: true,
token.CONST: true,
token.TYPE: true,
token.VAR: true,
token.GLOBAL: true,
}
var exprEnd = map[token.Token]bool{
......@@ -2458,6 +2459,11 @@ 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 {
......@@ -2564,7 +2570,7 @@ func (p *parser) parseDecl(sync map[token.Token]bool) ast.Decl {
var f parseSpecFunction
switch p.tok {
case token.CONST, token.VAR:
case token.CONST, token.VAR, token.GLOBAL:
f = p.parseValueSpec
case token.TYPE:
......
......@@ -106,6 +106,7 @@ const (
FOR
FUNC
GLOBAL
IF
IMPORT
......@@ -203,6 +204,7 @@ var tokens = [...]string{
FOR: "for",
FUNC: "func",
GLOBAL: "global",
IF: "if",
IMPORT: "import",
......
......@@ -39,8 +39,8 @@ var Typ = []*Basic{
Bool: {Bool, IsBoolean, "bool"},
Int: {Int, IsInteger, "int"},
Int8: {Int8, IsInteger, "int8"},
Int16: {Int16, IsInteger, "int16"},
Int8: {Int8, IsInteger, "__wa_int8"},
Int16: {Int16, IsInteger, "__wa_int16"},
Int32: {Int32, IsInteger, "int32"},
Int64: {Int64, IsInteger, "int64"},
Uint: {Uint, IsInteger | IsUnsigned, "uint"},
......@@ -70,8 +70,8 @@ var aliases = [...]*Basic{
{Byte, IsInteger | IsUnsigned, "字"},
{Rune, IsInteger, "rune"},
{Int8, IsInteger, "i8"},
{Int16, IsInteger, "i16"},
{Int8, IsInteger, "__wa_i8"},
{Int16, IsInteger, "__wa_i16"},
{Int32, IsInteger, "i32"},
{Int64, IsInteger, "i64"},
{Int, IsInteger, "数"},
......
// 版权 @2023 凹语言 作者。保留所有权利。
package wabt
import (
"bytes"
"errors"
"os"
"os/exec"
"path/filepath"
"sync"
"wa-lang.org/wa/internal/config"
"wa-lang.org/wa/internal/logger"
"wa-lang.org/wabt-go"
)
var muWabt sync.Mutex
var wat2wasmPath string
func init() {
const baseName = "wa.wat2wasm.exe"
// 1. exe 同级目录存在 wat2wasm ?
wat2wasmPath = filepath.Join(curExeDir(), baseName)
if exeExists(wat2wasmPath) {
return
}
// 2. 当前目录存在 wat2wasm ?
cwd, _ := os.Getwd()
wat2wasmPath = filepath.Join(cwd, baseName)
if exeExists(wat2wasmPath) {
return
}
// 3. 本地系统存在 wat2wasm ?
if s, _ := exec.LookPath(baseName); s != "" {
wat2wasmPath = s
return
}
// 4. wat2wasm 安装到 exe 所在目录 ?
wat2wasmPath = filepath.Join(curExeDir(), baseName)
if err := os.WriteFile(wat2wasmPath, wabt.LoadWat2Wasm(), 0777); err != nil {
logger.Tracef(&config.EnableTrace_app, "install wat2wasm failed: %+v", err)
return
}
}
func Wat2Wasm(watBytes []byte) (wasmBytes []byte, err error) {
muWabt.Lock()
defer muWabt.Unlock()
if wat2wasmPath == "" {
logger.Tracef(&config.EnableTrace_app, "wat2wasm not found")
return nil, errors.New("wat2wasm not found")
}
var bufStdout bytes.Buffer
var bufStderr bytes.Buffer
// wat2wasm - --output=-
cmd := exec.Command(wat2wasmPath, "-", "--output=-")
cmd.Stdin = bytes.NewReader(watBytes)
cmd.Stdout = &bufStdout
cmd.Stderr = &bufStderr
err = cmd.Run()
wasmBytes = bufStdout.Bytes()
if err != nil && bufStderr.Len() > 0 {
err = errors.New(bufStderr.String())
}
return
}
// exe 文件存在
func exeExists(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
if !fi.Mode().IsRegular() {
return false
}
return true
}
// 当前执行程序所在目录
func curExeDir() string {
s, err := os.Executable()
if err != nil {
logger.Panicf("os.Executable() failed: %+v", err)
}
return filepath.Dir(s)
}
// 版权 @2023 凹语言 作者。保留所有权利。
package wabt_test
import (
"strings"
"testing"
"wa-lang.org/wa/api"
"wa-lang.org/wa/internal/wabt"
)
func TestWat2Wasm(t *testing.T) {
for i, tt := range watTests {
_, err := wabt.Wat2Wasm([]byte(tt.watCode))
if !tMatchErrMsg(err, tt.errMsg) {
t.Fatalf("%d: check failed: %v", i, err)
}
}
}
func BenchmarkWat2Wasm(b *testing.B) {
wat := tBuildWat(t_hello_wa)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := wabt.Wat2Wasm([]byte(wat)); err != nil {
b.Fatal(err)
}
}
}
func tMatchErrMsg(err error, errMsg string) bool {
if errMsg == "" {
return err == nil
}
return strings.Contains(err.Error(), errMsg)
}
func tBuildWat(waCode string) string {
watBytes, err := api.BuildFile(api.DefaultConfig(), "main.wa", waCode)
if err != nil {
return err.Error()
}
return string(watBytes)
}
var watTests = []struct {
watCode string
errMsg string
}{
{
watCode: ``,
errMsg: `error: unexpected token "EOF", expected a module field or a module.`,
},
{
watCode: `()`,
errMsg: `error: unexpected token ")", expected a module field or a module.`,
},
{
watCode: `(module)`,
errMsg: "",
},
{
watCode: `
(module
(func)
(memory 1)
)`,
errMsg: "",
},
{
watCode: t_hello_wasi,
errMsg: "",
},
{
watCode: tBuildWat(t_hello_wa),
errMsg: "",
},
}
const t_hello_wasi = `
(module $hello_wasi
;; type iov struct { iov_base, iov_len int32 }
;; func fd_write(id *iov, iovs_len int32, nwritten *int32) (written int32)
(import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
(memory 1)(export "memory" (memory 0))
;; 前 8 个字节保留给 iov 数组, 字符串从地址 8 开始
(data (i32.const 8) "hello world\n")
;; _start 类似 main 函数, 自动执行
(func $main (export "_start")
(i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - 字符串地址为 8
(i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - 字符串长度
(call $fd_write
(i32.const 1) ;; 1 对应 stdout
(i32.const 0) ;; *iovs - 前 8 个字节保留给 iov 数组
(i32.const 1) ;; len(iovs) - 只有1个字符串
(i32.const 20) ;; nwritten - 指针, 里面是要写到数据长度
)
drop ;; 忽略返回值
)
)
`
const t_hello_wa = `
// 版权 @2019 凹语言 作者。保留所有权利。
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
}
`
版权 @{{.Year}} {{.Name}} 作者。保留所有权利。
# Pkg: {{.Name}}
欢迎使用 Wa 语言!
```
+---+ +---+
| o | | o |
| +----+ |
| |
| \/\/ |
| |
+------------+
```
// 版权 @{{.Year}} {{.Name}} 作者。保留所有权利。
func main {
println("你好,凹语言!")
println(sum(100))
heart()
}
func sum(n: int) => int {
var v: int
for i := 1; i <= n; i++ {
v += i
}
return v
}
func heart {
a := 0.0
for y := 1.5; y > -1.5; y = y - 0.1 {
for x := -1.5; x < 1.5; x = x + 0.05 {
a = x*x + y*y - 1.0
if a*a*a < x*x*y*y*y {
print('@')
} else {
print(' ')
}
}
println()
}
}
// 版权 @{{.Year}} {{.Name}} 作者。保留所有权利。
// 打印素数
func PrintPrime(max: int) {
for n := 2; n <= max; n = n + 1 {
isPrime := 1
for i := 2; i*i <= n; i = i + 1 {
if x := n % i; x == 0 {
isPrime = 0
}
}
if isPrime != 0 {
println(n)
}
}
}
// 版权 @{{.Year}} {{.Name}} 作者。保留所有权利。
// 打印整数
func Println(x: int) {
println(x)
}
{
"name": "{{.Name}}",
"pkgpath": "{{.Pkgpath}}",
"version": "0.0.1",
"authors": ["author", "author2"],
"description": "module description",
"readme": "README.md",
"homepage": "",
"repository": "",
"license": "",
"license_file": "LICENSE",
"keywords": [],
"categories": []
}
// 版权 @{{.Year}} {{.Name}} 作者。保留所有权利。
func Println(x: int) {
println(x)
}
// 版权 @2023 凹语言 作者。保留所有权利。
// Package binary implements simple translation between numbers and byte
// sequences and encoding and decoding of varints.
//
// Numbers are translated by reading and writing fixed-size values.
// A fixed-size value is either a fixed-size arithmetic
// type (bool, int8, uint8, int16, float32, complex64, ...)
// or an array or struct containing only fixed-size values.
//
// The varint functions encode and decode single integer values using
// a variable-length encoding; smaller values require fewer bytes.
// For a specification, see
// https://developers.google.com/protocol-buffers/docs/encoding.
//
// This package favors simplicity over efficiency. Clients that require
// high-performance serialization, especially for large data structures,
// should look at more advanced solutions such as the encoding/gob
// package or protocol buffers.
import (
//"errors"
//"io"
//"math"
)
// A ByteOrder specifies how to convert byte sequences into
// 16-, 32-, or 64-bit unsigned integers.
type ByteOrder interface {
Uint16(b: []byte) => u16
Uint32(b: []byte) => u32
Uint64(b: []byte) => u64
PutUint16(b: []byte, v: u16)
PutUint32(b: []byte, v: u32)
PutUint64(b: []byte, v: u64)
String() => string
}
// LittleEndian is the little-endian implementation of ByteOrder.
var LittleEndian = &littleEndian{}
// BigEndian is the big-endian implementation of ByteOrder.
var BigEndian = &bigEndian{}
type littleEndian struct{}
func littleEndian.Uint16(b: []byte) => u16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return u16(b[0]) | u16(b[1])<<8
}
func littleEndian.PutUint16(b: []byte, v: u16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
}
func littleEndian.Uint32(b: []byte) => u32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return u32(b[0]) | u32(b[1])<<8 | u32(b[2])<<16 | u32(b[3])<<24
}
func littleEndian.PutUint32(b: []byte, v: u32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
}
func littleEndian.Uint64(b: []byte) => u64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return u64(b[0]) | u64(b[1])<<8 | u64(b[2])<<16 | u64(b[3])<<24 |
u64(b[4])<<32 | u64(b[5])<<40 | u64(b[6])<<48 | u64(b[7])<<56
}
func littleEndian.PutUint64(b: []byte, v: u64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
}
func littleEndian.String => string { return "LittleEndian" }
func littleEndian.WaString => string { return "binary.LittleEndian" }
type bigEndian struct{}
func bigEndian.Uint16(b: []byte) => u16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return u16(b[1]) | u16(b[0])<<8
}
func bigEndian.PutUint16(b: []byte, v: u16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func bigEndian.Uint32(b: []byte) => u32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return u32(b[3]) | u32(b[2])<<8 | u32(b[1])<<16 | u32(b[0])<<24
}
func bigEndian.PutUint32(b: []byte, v: u32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func bigEndian.Uint64(b: []byte) => u64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return u64(b[7]) | u64(b[6])<<8 | u64(b[5])<<16 | u64(b[4])<<24 |
u64(b[3])<<32 | u64(b[2])<<40 | u64(b[1])<<48 | u64(b[0])<<56
}
func bigEndian.PutUint64(b: []byte, v: u64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}
func bigEndian.String => string { return "BigEndian" }
func bigEndian.WaString => string { return "binary.BigEndian" }
/*
// Read reads structured binary data from r into data.
// Data must be a pointer to a fixed-size value or a slice
// of fixed-size values.
// Bytes read from r are decoded using the specified byte order
// and written to successive fields of the data.
// When decoding boolean values, a zero byte is decoded as false, and
// any other non-zero byte is decoded as true.
// When reading into structs, the field data for fields with
// blank (_) field names is skipped; i.e., blank field names
// may be used for padding.
// When reading into a struct, all non-blank fields must be exported
// or Read may panic.
//
// The error is EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// Read returns ErrUnexpectedEOF.
func Read(r io.Reader, order ByteOrder, data interface{}) error {
// Fast path for basic types and slices.
if n := intDataSize(data); n != 0 {
bs := make([]byte, n)
if _, err := io.ReadFull(r, bs); err != nil {
return err
}
switch data := data.(type) {
case *bool:
*data = bs[0] != 0
case *int8:
*data = int8(bs[0])
case *uint8:
*data = bs[0]
case *int16:
*data = int16(order.Uint16(bs))
case *u16:
*data = order.Uint16(bs)
case *int32:
*data = int32(order.Uint32(bs))
case *u32:
*data = order.Uint32(bs)
case *int64:
*data = int64(order.Uint64(bs))
case *u64:
*data = order.Uint64(bs)
case *float32:
*data = math.Float32frombits(order.Uint32(bs))
case *float64:
*data = math.Float64frombits(order.Uint64(bs))
case []bool:
for i, x := range bs { // Easier to loop over the input for 8-bit values.
data[i] = x != 0
}
case []int8:
for i, x := range bs {
data[i] = int8(x)
}
case []uint8:
copy(data, bs)
case []int16:
for i := range data {
data[i] = int16(order.Uint16(bs[2*i:]))
}
case []u16:
for i := range data {
data[i] = order.Uint16(bs[2*i:])
}
case []int32:
for i := range data {
data[i] = int32(order.Uint32(bs[4*i:]))
}
case []u32:
for i := range data {
data[i] = order.Uint32(bs[4*i:])
}
case []int64:
for i := range data {
data[i] = int64(order.Uint64(bs[8*i:]))
}
case []u64:
for i := range data {
data[i] = order.Uint64(bs[8*i:])
}
case []float32:
for i := range data {
data[i] = math.Float32frombits(order.Uint32(bs[4*i:]))
}
case []float64:
for i := range data {
data[i] = math.Float64frombits(order.Uint64(bs[8*i:]))
}
default:
n = 0 // fast path doesn't apply
}
if n != 0 {
return nil
}
}
// Fallback to reflect-based decoding.
v := reflect.ValueOf(data)
size := -1
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
size = dataSize(v)
case reflect.Slice:
size = dataSize(v)
}
if size < 0 {
return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String())
}
d := &decoder{order: order, buf: make([]byte, size)}
if _, err := io.ReadFull(r, d.buf); err != nil {
return err
}
d.value(v)
return nil
}
// Write writes the binary representation of data into w.
// Data must be a fixed-size value or a slice of fixed-size
// values, or a pointer to such data.
// Boolean values encode as one byte: 1 for true, and 0 for false.
// Bytes written to w are encoded using the specified byte order
// and read from successive fields of the data.
// When writing structs, zero values are written for fields
// with blank (_) field names.
func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fast path for basic types and slices.
if n := intDataSize(data); n != 0 {
bs := make([]byte, n)
switch v := data.(type) {
case *bool:
if *v {
bs[0] = 1
} else {
bs[0] = 0
}
case bool:
if v {
bs[0] = 1
} else {
bs[0] = 0
}
case []bool:
for i, x := range v {
if x {
bs[i] = 1
} else {
bs[i] = 0
}
}
case *int8:
bs[0] = byte(*v)
case int8:
bs[0] = byte(v)
case []int8:
for i, x := range v {
bs[i] = byte(x)
}
case *uint8:
bs[0] = *v
case uint8:
bs[0] = v
case []uint8:
bs = v
case *int16:
order.PutUint16(bs, u16(*v))
case int16:
order.PutUint16(bs, u16(v))
case []int16:
for i, x := range v {
order.PutUint16(bs[2*i:], u16(x))
}
case *u16:
order.PutUint16(bs, *v)
case u16:
order.PutUint16(bs, v)
case []u16:
for i, x := range v {
order.PutUint16(bs[2*i:], x)
}
case *int32:
order.PutUint32(bs, u32(*v))
case int32:
order.PutUint32(bs, u32(v))
case []int32:
for i, x := range v {
order.PutUint32(bs[4*i:], u32(x))
}
case *u32:
order.PutUint32(bs, *v)
case u32:
order.PutUint32(bs, v)
case []u32:
for i, x := range v {
order.PutUint32(bs[4*i:], x)
}
case *int64:
order.PutUint64(bs, u64(*v))
case int64:
order.PutUint64(bs, u64(v))
case []int64:
for i, x := range v {
order.PutUint64(bs[8*i:], u64(x))
}
case *u64:
order.PutUint64(bs, *v)
case u64:
order.PutUint64(bs, v)
case []u64:
for i, x := range v {
order.PutUint64(bs[8*i:], x)
}
case *float32:
order.PutUint32(bs, math.Float32bits(*v))
case float32:
order.PutUint32(bs, math.Float32bits(v))
case []float32:
for i, x := range v {
order.PutUint32(bs[4*i:], math.Float32bits(x))
}
case *float64:
order.PutUint64(bs, math.Float64bits(*v))
case float64:
order.PutUint64(bs, math.Float64bits(v))
case []float64:
for i, x := range v {
order.PutUint64(bs[8*i:], math.Float64bits(x))
}
}
_, err := w.Write(bs)
return err
}
// Fallback to reflect-based encoding.
v := reflect.Indirect(reflect.ValueOf(data))
size := dataSize(v)
if size < 0 {
return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String())
}
buf := make([]byte, size)
e := &encoder{order: order, buf: buf}
e.value(v)
_, err := w.Write(buf)
return err
}
// Size returns how many bytes Write would generate to encode the value v, which
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
// If v is neither of these, Size returns -1.
func Size(v interface{}) int {
return dataSize(reflect.Indirect(reflect.ValueOf(v)))
}
var structSize sync.Map // map[reflect.Type]int
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
// it returns the length of the slice times the element size and does not count the memory
// occupied by the header. If the type of v is not acceptable, dataSize returns -1.
func dataSize(v reflect.Value) int {
switch v.Kind() {
case reflect.Slice:
if s := sizeof(v.Type().Elem()); s >= 0 {
return s * v.Len()
}
return -1
case reflect.Struct:
t := v.Type()
if size, ok := structSize.Load(t); ok {
return size.(int)
}
size := sizeof(t)
structSize.Store(t, size)
return size
default:
return sizeof(v.Type())
}
}
// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable.
func sizeof(t reflect.Type) int {
switch t.Kind() {
case reflect.Array:
if s := sizeof(t.Elem()); s >= 0 {
return s * t.Len()
}
case reflect.Struct:
sum := 0
for i, n := 0, t.NumField(); i < n; i++ {
s := sizeof(t.Field(i).Type)
if s < 0 {
return -1
}
sum += s
}
return sum
case reflect.Bool,
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
return int(t.Size())
}
return -1
}
type coder struct {
order ByteOrder
buf []byte
offset int
}
type decoder coder
type encoder coder
func (d *decoder) bool() bool {
x := d.buf[d.offset]
d.offset++
return x != 0
}
func (e *encoder) bool(x bool) {
if x {
e.buf[e.offset] = 1
} else {
e.buf[e.offset] = 0
}
e.offset++
}
func (d *decoder) uint8() uint8 {
x := d.buf[d.offset]
d.offset++
return x
}
func (e *encoder) uint8(x uint8) {
e.buf[e.offset] = x
e.offset++
}
func (d *decoder) u16() u16 {
x := d.order.Uint16(d.buf[d.offset : d.offset+2])
d.offset += 2
return x
}
func (e *encoder) u16(x u16) {
e.order.PutUint16(e.buf[e.offset:e.offset+2], x)
e.offset += 2
}
func (d *decoder) u32() u32 {
x := d.order.Uint32(d.buf[d.offset : d.offset+4])
d.offset += 4
return x
}
func (e *encoder) u32(x u32) {
e.order.PutUint32(e.buf[e.offset:e.offset+4], x)
e.offset += 4
}
func (d *decoder) u64() u64 {
x := d.order.Uint64(d.buf[d.offset : d.offset+8])
d.offset += 8
return x
}
func (e *encoder) u64(x u64) {
e.order.PutUint64(e.buf[e.offset:e.offset+8], x)
e.offset += 8
}
func (d *decoder) int8() int8 { return int8(d.uint8()) }
func (e *encoder) int8(x int8) { e.uint8(uint8(x)) }
func (d *decoder) int16() int16 { return int16(d.u16()) }
func (e *encoder) int16(x int16) { e.u16(u16(x)) }
func (d *decoder) int32() int32 { return int32(d.u32()) }
func (e *encoder) int32(x int32) { e.u32(u32(x)) }
func (d *decoder) int64() int64 { return int64(d.u64()) }
func (e *encoder) int64(x int64) { e.u64(u64(x)) }
func (d *decoder) value(v reflect.Value) {
switch v.Kind() {
case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
d.value(v.Index(i))
}
case reflect.Struct:
t := v.Type()
l := v.NumField()
for i := 0; i < l; i++ {
// Note: Calling v.CanSet() below is an optimization.
// It would be sufficient to check the field name,
// but creating the StructField info for each field is
// costly (run "go test -bench=ReadStruct" and compare
// results when making changes to this code).
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
d.value(v)
} else {
d.skip(v)
}
}
case reflect.Slice:
l := v.Len()
for i := 0; i < l; i++ {
d.value(v.Index(i))
}
case reflect.Bool:
v.SetBool(d.bool())
case reflect.Int8:
v.SetInt(int64(d.int8()))
case reflect.Int16:
v.SetInt(int64(d.int16()))
case reflect.Int32:
v.SetInt(int64(d.int32()))
case reflect.Int64:
v.SetInt(d.int64())
case reflect.Uint8:
v.SetUint(u64(d.uint8()))
case reflect.Uint16:
v.SetUint(u64(d.u16()))
case reflect.Uint32:
v.SetUint(u64(d.u32()))
case reflect.Uint64:
v.SetUint(d.u64())
case reflect.Float32:
v.SetFloat(float64(math.Float32frombits(d.u32())))
case reflect.Float64:
v.SetFloat(math.Float64frombits(d.u64()))
case reflect.Complex64:
v.SetComplex(complex(
float64(math.Float32frombits(d.u32())),
float64(math.Float32frombits(d.u32())),
))
case reflect.Complex128:
v.SetComplex(complex(
math.Float64frombits(d.u64()),
math.Float64frombits(d.u64()),
))
}
}
func (e *encoder) value(v reflect.Value) {
switch v.Kind() {
case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
e.value(v.Index(i))
}
case reflect.Struct:
t := v.Type()
l := v.NumField()
for i := 0; i < l; i++ {
// see comment for corresponding code in decoder.value()
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
e.value(v)
} else {
e.skip(v)
}
}
case reflect.Slice:
l := v.Len()
for i := 0; i < l; i++ {
e.value(v.Index(i))
}
case reflect.Bool:
e.bool(v.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch v.Type().Kind() {
case reflect.Int8:
e.int8(int8(v.Int()))
case reflect.Int16:
e.int16(int16(v.Int()))
case reflect.Int32:
e.int32(int32(v.Int()))
case reflect.Int64:
e.int64(v.Int())
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch v.Type().Kind() {
case reflect.Uint8:
e.uint8(uint8(v.Uint()))
case reflect.Uint16:
e.u16(u16(v.Uint()))
case reflect.Uint32:
e.u32(u32(v.Uint()))
case reflect.Uint64:
e.u64(v.Uint())
}
case reflect.Float32, reflect.Float64:
switch v.Type().Kind() {
case reflect.Float32:
e.u32(math.Float32bits(float32(v.Float())))
case reflect.Float64:
e.u64(math.Float64bits(v.Float()))
}
case reflect.Complex64, reflect.Complex128:
switch v.Type().Kind() {
case reflect.Complex64:
x := v.Complex()
e.u32(math.Float32bits(float32(real(x))))
e.u32(math.Float32bits(float32(imag(x))))
case reflect.Complex128:
x := v.Complex()
e.u64(math.Float64bits(real(x)))
e.u64(math.Float64bits(imag(x)))
}
}
}
func (d *decoder) skip(v reflect.Value) {
d.offset += dataSize(v)
}
func (e *encoder) skip(v reflect.Value) {
n := dataSize(v)
zero := e.buf[e.offset : e.offset+n]
for i := range zero {
zero[i] = 0
}
e.offset += n
}
// intDataSize returns the size of the data required to represent the data when encoded.
// It returns zero if the type cannot be implemented by the fast path in Read or Write.
func intDataSize(data interface{}) int {
switch data := data.(type) {
case bool, int8, uint8, *bool, *int8, *uint8:
return 1
case []bool:
return len(data)
case []int8:
return len(data)
case []uint8:
return len(data)
case int16, u16, *int16, *u16:
return 2
case []int16:
return 2 * len(data)
case []u16:
return 2 * len(data)
case int32, u32, *int32, *u32:
return 4
case []int32:
return 4 * len(data)
case []u32:
return 4 * len(data)
case int64, u64, *int64, *u64:
return 8
case []int64:
return 8 * len(data)
case []u64:
return 8 * len(data)
case float32, *float32:
return 4
case float64, *float64:
return 8
case []float32:
return 4 * len(data)
case []float64:
return 8 * len(data)
}
return 0
}
*/
\ No newline at end of file
// 版权 @2023 凹语言 作者。保留所有权利。
// Equal reports whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b: []byte) => bool {
// Neither cmd/compile nor gccgo allocates for these string conversions.
return string(a) == string(b)
}
// 忽略英文大小写比较
func EqualFold(s, t: []byte) => bool {
if len(s) != len(t) {
return false
}
if len(s) == 0 {
return true
}
for i := 0; i < len(s); i++ {
if toupper(s[i]) != toupper(t[i]) {
return false
}
}
return true
}
func toupper(c: byte) => byte {
if c >= 'a' && c <= 'z' {
return c - 32
}
return c
}
func tolower(c: byte) => byte {
if c >= 'A' && c <= 'Z' {
return c + 32
}
return c
}
\ No newline at end of file
// 版权 @2023 凹语言 作者。保留所有权利。
func TestEqual {
for _, tt := range compareTests {
eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
println(string(tt.a), string(tt.b), eql)
assert(false, `Equal failed`)
}
}
}
func TestEqualFold {
for _, tt := range EqualFoldTests {
if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out {
println(string(tt.s), string(tt.t))
assert(false, `EqualFold failed`)
}
if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out {
println(string(tt.s), string(tt.t))
assert(false, `EqualFold failed`)
}
}
}
var EqualFoldTests = []struct {
s, t string
out bool
}{
{"abc", "abc", true},
{"ABcd", "ABcd", true},
{"123abc", "123ABC", true},
{"abc", "xyz", false},
{"abc", "XYZ", false},
{"abcdefghijk", "abcdefghijX", false},
}
var compareTests = []struct {
a, b []byte
i int
}{
{[]byte(""), []byte(""), 0},
{[]byte("a"), []byte(""), 1},
{[]byte(""), []byte("a"), -1},
{[]byte("abc"), []byte("abc"), 0},
{[]byte("abd"), []byte("abc"), 1},
{[]byte("abc"), []byte("abd"), -1},
{[]byte("ab"), []byte("abc"), -1},
{[]byte("abc"), []byte("ab"), 1},
{[]byte("x"), []byte("ab"), 1},
{[]byte("ab"), []byte("x"), -1},
{[]byte("x"), []byte("a"), 1},
{[]byte("b"), []byte("x"), -1},
// test runtime·memeq's chunked implementation
{[]byte("abcdefgh"), []byte("abcdefgh"), 0},
{[]byte("abcdefghi"), []byte("abcdefghi"), 0},
{[]byte("abcdefghi"), []byte("abcdefghj"), -1},
{[]byte("abcdefghj"), []byte("abcdefghi"), 1},
// nil tests
{nil, nil, 0},
{[]byte(""), nil, 0},
{nil, []byte(""), 0},
{[]byte("a"), nil, 1},
{nil, []byte("a"), -1},
}
// 版权 @2023 凹语言 作者。保留所有权利。
func New(text: string) => error {
return &errorString{text}
}
type errorString struct {
s :string
}
func errorString.Error => string {
return this.s
}
// 版权 @2023 凹语言 作者。保留所有权利。
import "fmt"
func TestNewEqual {
// Different allocations should not be equal.
if New("abc") == New("abc") {
assert(false, `New("abc") == New("abc")`)
}
if New("abc") == New("xyz") {
assert(false, `New("abc") == New("xyz")`)
}
// Same allocation should be equal to itself (not crash).
err := New("jkl")
if err != err {
assert(false, `err != err`)
}
}
func TestErrorMethod {
err := New("abc")
if err.Error() != "abc" {
assert(false, `New("abc").Error() = "` + err.Error() + `", want "abc"`)
}
}
func ExampleNew() {
err := New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Println(err)
}
// Output:
// emit macho dwarf: elf header corrupted
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。