...
 
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
}
// 编译为 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
}
}
fmt.Println(err)
os.Exit(1)
}
conf := wazero.NewModuleConfig().
WithStdout(stdoutBuffer).
WithStderr(stderrBuffer).
WithStdin(os.Stdin).
WithRandSource(rand.Reader).
WithSysNanosleep().
WithSysNanotime().
WithSysWalltime().
WithArgs("a.out.wasm").
WithName("unittest")
// wat 落盘(仅用于调试)
os.WriteFile("a.out.wat", []byte(watOutput), 0666)
// 执行 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 != "" {
if len(stdout) > 0 {
if s := sWithPrefix(string(stdout), " "); s != "" {
fmt.Println(s)
}
return err
}
stdout := stdoutBuffer.Bytes()
stderr := stderrBuffer.Bytes()
if len(stderr) > 0 {
if s := sWithPrefix(string(stderr), " "); s != "" {
fmt.Println(s)
}
}
os.Exit(1)
}
stdout = bytes.TrimSpace(stdout)
if t.Output != "" && t.Output == string(stdout) {
......@@ -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
}
stdout, stderr, err := apputil.RunWasmEx(cfg, "a.out.wat", appArgs...)
os.Exit(1)
}
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,7 +504,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
xt := x.Type()
switch {
case typ.Equal(m.I8):
/*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):
......@@ -483,12 +519,12 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return
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,7 +540,7 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
insts = append(insts, wat.NewInstAnd(wat.I32{}))
return
case typ.Equal(m.I16):
/*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):
......@@ -519,12 +555,12 @@ func (m *Module) EmitGenConvert(x Value, typ ValueType) (insts []wat.Inst) {
case xt.Equal(m.F64):
insts = append(insts, wat.NewInstConvert_i32_trunc_f64_s())
}
return
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
......
......@@ -500,6 +500,7 @@ var declStart = map[token.Token]bool{
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
}
// 版权 @2019 凹语言 作者。保留所有权利。
#wa:build !fmt_tag
func Println(a: ...interface{}) {
for i, v := range a {
if i > 0 {
print(" ")
}
switch v:= v.(type) {
case int:
print(v)
case string:
print(v)
case error:
print(v.Error())
}
}
println()
}
// 版权 @2023 凹语言 作者。保留所有权利。
#wa:build fmt_tag
func Println(x: int) {
println("fmt_tag:", x)
}
// 版权 @2023 凹语言 作者。保留所有权利。
func TestOK_assert {
ok := true
assert(ok)
assert(true, "message")
}
func TestOK_output {
println("abc 123")
// Output:
// abc 123
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。