Compare commits
301 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
b08ea25c20 | |
|
|
9f22163ff7 | |
|
|
d335923432 | |
|
|
33d3826a3c | |
|
|
20fe893009 | |
|
|
7a8bb12296 | |
|
|
f361d89241 | |
|
|
e3e895acb2 | |
|
|
7484fe5903 | |
|
|
a5f59fa675 | |
|
|
b73b48f874 | |
|
|
adc0a1a791 | |
|
|
af5f8eca1d | |
|
|
fa7fa0ff8c | |
|
|
275e0124ad | |
|
|
8913805d36 | |
|
|
4b770dc521 | |
|
|
6290692a7b | |
|
|
7ef84c74ae | |
|
|
740948bcbb | |
|
|
b37d27cb47 | |
|
|
1194904bd8 | |
|
|
9b07a5afea | |
|
|
b8b6c5918c | |
|
|
7979cc4ff5 | |
|
|
e10dd3ffc2 | |
|
|
daffa01c7b | |
|
|
7bea718e2e | |
|
|
c7e79d4fe6 | |
|
|
510af7ef6c | |
|
|
562a7b5e59 | |
|
|
dca847159d | |
|
|
f69e21a85e | |
|
|
3c34f0db3b | |
|
|
1627f93edb | |
|
|
c7210adcec | |
|
|
2daf947ea9 | |
|
|
29767745d5 | |
|
|
3efa7341c6 | |
|
|
7d6cc8925d | |
|
|
4fea6923f3 | |
|
|
b645f8d8e4 | |
|
|
481f6878c3 | |
|
|
fc0314a54b | |
|
|
c312135ccb | |
|
|
c0b58d678b | |
|
|
afddc41fc9 | |
|
|
6daa480a76 | |
|
|
5296334131 | |
|
|
32ab9ab3d3 | |
|
|
f3476b281f | |
|
|
21d2a2c7cc | |
|
|
48b61ab9ea | |
|
|
e1b61af9af | |
|
|
e8c9eefaae | |
|
|
d9af33bb75 | |
|
|
6cbf43a141 | |
|
|
6207648869 | |
|
|
ab40ca7aec | |
|
|
d6c5c21a68 | |
|
|
53f9d895c8 | |
|
|
deaa7f87c0 | |
|
|
33f6724e76 | |
|
|
a03e731d98 | |
|
|
7f484df65b | |
|
|
37299bc094 | |
|
|
6157ec2a23 | |
|
|
e3145d88e8 | |
|
|
1329c4a0f2 | |
|
|
6017de5bec | |
|
|
ee56900e3e | |
|
|
e556debf78 | |
|
|
90a0504467 | |
|
|
d4dd989861 | |
|
|
b257139578 | |
|
|
fe8be85b56 | |
|
|
f4b3765204 | |
|
|
ae1ddc3798 | |
|
|
a27b5d5eaa | |
|
|
4277b7f28d | |
|
|
c06198e1c6 | |
|
|
cd3645e40a | |
|
|
b4eb99057b | |
|
|
619098ff90 | |
|
|
ee0c4c718d | |
|
|
6557bef692 | |
|
|
91ecd0c297 | |
|
|
69aaa0d61f | |
|
|
34dbf326f0 | |
|
|
28ba454b25 | |
|
|
7cb70a8d9b | |
|
|
14a0ac062c | |
|
|
431facd2d7 | |
|
|
80bc37c983 | |
|
|
baf0f798e2 | |
|
|
ac3d716b58 | |
|
|
7a4d1e6b8a | |
|
|
740912a32e | |
|
|
1c9855f2c7 | |
|
|
c1b8fc1e62 | |
|
|
61c1638260 | |
|
|
fb6798f38d | |
|
|
88316e15a4 | |
|
|
c5ce3a5e54 | |
|
|
9e0cc0f9ec | |
|
|
8865306912 | |
|
|
154dc2d591 | |
|
|
bff4fd1883 | |
|
|
7ee8e6279b | |
|
|
36381d5adc | |
|
|
40385e8cd2 | |
|
|
fc25830c3a | |
|
|
fb6b8c64da | |
|
|
2678407bfb | |
|
|
744e345703 | |
|
|
c0085c32ec | |
|
|
9f8486566b | |
|
|
3693430e17 | |
|
|
66113f0f2c | |
|
|
e53ddc2616 | |
|
|
4ca0062f02 | |
|
|
c2ceecece6 | |
|
|
4f04afc2d1 | |
|
|
37aabb322d | |
|
|
6f3da4c874 | |
|
|
9c615cf054 | |
|
|
dd68b06469 | |
|
|
d12c948e79 | |
|
|
fb2ead4b07 | |
|
|
931a8a7eb4 | |
|
|
338ffe0288 | |
|
|
17ee7a95e2 | |
|
|
8a020beaca | |
|
|
fa740ac138 | |
|
|
3c382a5cca | |
|
|
472677ce8b | |
|
|
3a69f29d32 | |
|
|
376369693c | |
|
|
7f4e6d11dc | |
|
|
1ed1108a3d | |
|
|
eea10fb266 | |
|
|
90458b4485 | |
|
|
9f54c60853 | |
|
|
708fa769b0 | |
|
|
5024b8a351 | |
|
|
2bb7def2c4 | |
|
|
46c94732a7 | |
|
|
bf9ca015f5 | |
|
|
7eb0fabac0 | |
|
|
0a813a7e24 | |
|
|
1dc7132856 | |
|
|
c8180183be | |
|
|
8ca5774db5 | |
|
|
c0d1e8bd73 | |
|
|
ae4622bb60 | |
|
|
9dc0ff6b3c | |
|
|
b65c8ff5d4 | |
|
|
b4c0b7c319 | |
|
|
24e76b871e | |
|
|
7c1573fbf3 | |
|
|
a65f4d1a6b | |
|
|
c79fbbc593 | |
|
|
d4c997cc1e | |
|
|
02345b4cc3 | |
|
|
f6c8d65d86 | |
|
|
a785c977c1 | |
|
|
fe27741c41 | |
|
|
340a1aa10d | |
|
|
ca0b095313 | |
|
|
fc10449a76 | |
|
|
8e2880af99 | |
|
|
13c849ddfe | |
|
|
0e0c4a7a2a | |
|
|
cdb0352bfe | |
|
|
acc3779a0a | |
|
|
e644df0204 | |
|
|
78f7b71fdb | |
|
|
7a06fb5289 | |
|
|
03ec841701 | |
|
|
bd7f9f25d5 | |
|
|
e9b52e3e8a | |
|
|
9558393432 | |
|
|
33fa620d2d | |
|
|
72136e07ca | |
|
|
2f5995a2d8 | |
|
|
92ec97e8ab | |
|
|
decb015cda | |
|
|
f714517661 | |
|
|
f018eb7c71 | |
|
|
43ca453794 | |
|
|
c957ea0f7d | |
|
|
48db574ba4 | |
|
|
7b220f4b9a | |
|
|
b0839d7a2c | |
|
|
dfa6b44319 | |
|
|
0a5a671f5d | |
|
|
86a784e019 | |
|
|
edaa2dd380 | |
|
|
9b9870b2f9 | |
|
|
08da080269 | |
|
|
14d79a0b71 | |
|
|
6cf3f999e0 | |
|
|
a61c9d839c | |
|
|
db9d81ad4d | |
|
|
bcadb47048 | |
|
|
52f3c77d37 | |
|
|
0a31dc803b | |
|
|
8201374e17 | |
|
|
61571ac7b0 | |
|
|
be17597bdb | |
|
|
783d26fc35 | |
|
|
ca4bc40a3b | |
|
|
3a416ed2ac | |
|
|
106cf4c828 | |
|
|
5319e5a5ed | |
|
|
473e0fcfe6 | |
|
|
4aa6f29533 | |
|
|
663c4ba3c8 | |
|
|
61d8ae100e | |
|
|
a72a04eaec | |
|
|
de4655625f | |
|
|
d93a368db5 | |
|
|
a21a345832 | |
|
|
bce3aefc36 | |
|
|
0b18402827 | |
|
|
013ecb93ec | |
|
|
b231910a56 | |
|
|
ad1a12c93b | |
|
|
199c5f366b | |
|
|
627abe9ef2 | |
|
|
0d4097dff6 | |
|
|
d8f793798e | |
|
|
6a26c56444 | |
|
|
e50b09b8eb | |
|
|
a94481008e | |
|
|
4fa3419bf7 | |
|
|
c26a74d3b9 | |
|
|
38243912d4 | |
|
|
9fb4ddc9c1 | |
|
|
c1cd6a8e59 | |
|
|
4b5efe60c3 | |
|
|
01432b1d59 | |
|
|
2da1c6e741 | |
|
|
4bb260a1d2 | |
|
|
09fdcecd00 | |
|
|
bc13e5ae2d | |
|
|
6b88c18440 | |
|
|
c13d6105dc | |
|
|
7f70a11954 | |
|
|
b4d1b285ad | |
|
|
ca2669a243 | |
|
|
31e72fa086 | |
|
|
48e0f65fa7 | |
|
|
7e5593b33d | |
|
|
8d0808160b | |
|
|
fed1a25a38 | |
|
|
babd47c447 | |
|
|
9b063fdf00 | |
|
|
52f85d9b40 | |
|
|
41c66eb123 | |
|
|
6ce77e2aaf | |
|
|
d0f333dd79 | |
|
|
8a0d245180 | |
|
|
0fa2cfa3b1 | |
|
|
6313174bbf | |
|
|
5a5d01f7a9 | |
|
|
ef4d8cc0d0 | |
|
|
ead196142d | |
|
|
659137b156 | |
|
|
6ec3274366 | |
|
|
9989d25590 | |
|
|
291b1e0039 | |
|
|
034be0d214 | |
|
|
4c29af9ece | |
|
|
122a60606c | |
|
|
595284752c | |
|
|
afb55fa475 | |
|
|
ced44de321 | |
|
|
6fbc4e7dc9 | |
|
|
8b5c03f633 | |
|
|
e5ed1490ab | |
|
|
d339f1a262 | |
|
|
9e01ec6025 | |
|
|
6b54f4f2b3 | |
|
|
6664ced0fc | |
|
|
a909302ed2 | |
|
|
672daee8e1 | |
|
|
8981da7fc8 | |
|
|
722753c9d6 | |
|
|
2822949d6d | |
|
|
c3b4e3bf4d | |
|
|
317a57b6e4 | |
|
|
5af6cc365b | |
|
|
67650658c7 | |
|
|
15244f9252 | |
|
|
3f601cc526 | |
|
|
0f564e94ec | |
|
|
0df1b138e5 | |
|
|
998e732710 | |
|
|
94ee10be7a | |
|
|
ee0ba1a42f |
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
go-version: 1.17
|
||||
|
||||
- run: make -C internal/app/wawasm
|
||||
- run: make -C _examples/snake publish
|
||||
- run: make -C waroot/examples/snake publish
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@ jobs:
|
|||
- run: go install
|
||||
|
||||
- run: wa -h
|
||||
- run: wa hello.wa
|
||||
- run: wa run _examples/prime
|
||||
- run: wa run hello.wa
|
||||
- run: wa run -target=chrome hello.wa
|
||||
- run: cd waroot && wa run examples/prime
|
||||
- run: cd waroot && wa run hello.wa
|
||||
- run: cd waroot && wa run -target=chrome hello.wa
|
||||
|
||||
- uses: shogo82148/actions-goveralls@v1
|
||||
with:
|
||||
|
|
@ -60,10 +59,9 @@ jobs:
|
|||
- run: go install
|
||||
|
||||
- run: wa -h
|
||||
- run: wa hello.wa
|
||||
- run: wa run _examples/prime
|
||||
- run: wa run hello.wa
|
||||
- run: wa run -target=chrome hello.wa
|
||||
- run: cd waroot && wa run examples/prime
|
||||
- run: cd waroot && wa run hello.wa
|
||||
- run: cd waroot && wa run -target=chrome hello.wa
|
||||
|
||||
build-and-test-macos:
|
||||
runs-on: macos-12
|
||||
|
|
@ -86,7 +84,6 @@ jobs:
|
|||
- run: go install
|
||||
|
||||
- run: wa -h
|
||||
- run: wa hello.wa
|
||||
- run: wa run _examples/prime
|
||||
- run: wa run hello.wa
|
||||
- run: wa run -target=chrome hello.wa
|
||||
- run: cd waroot && wa run examples/prime
|
||||
- run: cd waroot && wa run hello.wa
|
||||
- run: cd waroot && wa run -target=chrome hello.wa
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/.fleet
|
||||
/.vscode
|
||||
/_build*
|
||||
/hello
|
||||
|
||||
wa
|
||||
wa.exe
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
# This is an example .goreleaser.yml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
- go version
|
||||
builds:
|
||||
- id: default
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
# 使用 ldflags="-s -w" 去掉符号表和调试信息,以减少发布包的大小
|
||||
ldflags:
|
||||
- -s
|
||||
- -w
|
||||
ignore:
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
main: .
|
||||
archives:
|
||||
- id: default
|
||||
builds:
|
||||
- default
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
use: github
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: wa-lang
|
||||
name: wa
|
||||
draft: false
|
||||
footer: |
|
||||
Homepage https://wa-lang.org
|
||||
|
||||
brews:
|
||||
- tap:
|
||||
owner: wa-lang
|
||||
name: homebrew-tap
|
||||
url_template: "https://github.com/wa-lang/wa/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
download_strategy: CurlDownloadStrategy
|
||||
# Git author used to commit to the repository.
|
||||
# Defaults are shown.
|
||||
commit_author:
|
||||
name: GoReleaser Bot
|
||||
email: goreleaser@carlosbecker.com
|
||||
folder: HomebrewFormula
|
||||
homepage: "https://github.com/wa-lang/wa"
|
||||
description: "The Wa Programming Language"
|
||||
license: "AGPL-3.0 license"
|
||||
skip_upload: false
|
||||
test: |
|
||||
system "#{bin}/wa"
|
||||
|
||||
scoop:
|
||||
url_template: "https://github.com/wa-lang/wa/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
bucket:
|
||||
owner: wa-lang
|
||||
name: scoop-bucket
|
||||
branch: master
|
||||
token: "{{ .Env.GITHUB_TOKEN }}"
|
||||
folder: Scoops
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: bot@goreleaser.com
|
||||
commit_msg_template: "Scoop update for {{ .ProjectName }} version {{ .Tag }}"
|
||||
homepage: "http://github.com/wa-lang/wa"
|
||||
description: "The Wa Programming Language"
|
||||
license: AGPL-3.0 license
|
||||
|
|
@ -29,9 +29,9 @@ stages:
|
|||
chmod +X output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa -h
|
||||
./output/wa-lang-linux64/wa hello.wa
|
||||
./output/wa-lang-linux64/wa run _examples/prime
|
||||
./output/wa-lang-linux64/wa run hello.wa
|
||||
#./output/wa-lang-linux64/wa waroot/hello.wa
|
||||
#./output/wa-lang-linux64/wa run waroot/examples/prime
|
||||
#./output/wa-lang-linux64/wa run waroot/hello.wa
|
||||
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
||||
artifacts:
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ stages:
|
|||
chmod +X output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa -h
|
||||
./output/wa-lang-linux64/wa hello.wa
|
||||
./output/wa-lang-linux64/wa run _examples/prime
|
||||
./output/wa-lang-linux64/wa run hello.wa
|
||||
#./output/wa-lang-linux64/wa waroot/hello.wa
|
||||
#./output/wa-lang-linux64/wa run waroot/examples/prime
|
||||
#./output/wa-lang-linux64/wa run waroot/hello.wa
|
||||
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,24小时后自动清除
|
||||
artifacts:
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ stages:
|
|||
chmod +X output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa
|
||||
./output/wa-lang-linux64/wa -h
|
||||
./output/wa-lang-linux64/wa hello.wa
|
||||
./output/wa-lang-linux64/wa run _examples/prime
|
||||
./output/wa-lang-linux64/wa run hello.wa
|
||||
#./output/wa-lang-linux64/wa waroot/hello.wa
|
||||
#./output/wa-lang-linux64/wa run waroot/examples/prime
|
||||
#./output/wa-lang-linux64/wa run waroot/hello.wa
|
||||
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
||||
artifacts:
|
||||
|
|
|
|||
17
Makefile
|
|
@ -1,17 +1,15 @@
|
|||
# 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
hello:
|
||||
go run main.go run hello.wa
|
||||
go install
|
||||
cd waroot && go run ../main.go run hello.wa
|
||||
|
||||
prime:
|
||||
go run main.go run _examples/prime
|
||||
cd waroot && go run ../main.go run examples/prime
|
||||
|
||||
build-wasm:
|
||||
GOARCH=wasm GOOS=js go build -o wa.out.wasm ./main_wasm.go
|
||||
|
||||
win-exe-icon:
|
||||
windres -o main_rc_windows.syso main.rc
|
||||
|
||||
arduino-run:
|
||||
go run main.go -target=arduino arduino.wa
|
||||
|
||||
|
|
@ -21,14 +19,17 @@ arduino-build:
|
|||
xxd -i a.out.wasm > app.wasm.h
|
||||
|
||||
ci-test-all:
|
||||
go install
|
||||
go test ./...
|
||||
|
||||
@echo "== std test begin =="
|
||||
go run main.go test fmt
|
||||
go run main.go test std
|
||||
@echo "== std ok =="
|
||||
|
||||
go run main.go hello.wa
|
||||
go run main.go run ./waroot/hello.wa
|
||||
cd waroot && go run ../main.go run hello.wa
|
||||
|
||||
make -C ./_examples ci-test-all
|
||||
make -C ./waroot/examples ci-test-all
|
||||
|
||||
clean:
|
||||
-rm a.out*
|
||||
|
|
|
|||
43
README-zh.md
|
|
@ -10,6 +10,7 @@
|
|||

|
||||
|
||||
- 主页: [https://wa-lang.org](https://wa-lang.org)
|
||||
- 参考手册: [https://wa-lang.org/man/](https://wa-lang.org/man/)
|
||||
- 仓库(Gitee): [https://gitee.com/wa-lang/wa](https://gitee.com/wa-lang/wa)
|
||||
- 仓库(Github): [https://github.com/wa-lang/wa](https://github.com/wa-lang/wa)
|
||||
- Playground: [https://wa-lang.org/playground](https://wa-lang.org/playground)
|
||||
|
|
@ -36,14 +37,6 @@
|
|||
|
||||

|
||||
|
||||
## 本地安装和测试:
|
||||
|
||||
Go >= 1.17
|
||||
|
||||
1. `go install wa-lang.org/wa@latest`
|
||||
2. `wa init -name=_examples/hi`
|
||||
3. `wa run _examples/hi`
|
||||
|
||||
## 例子: 凹语言
|
||||
|
||||
打印字符和调用函数:
|
||||
|
|
@ -51,9 +44,11 @@ Go >= 1.17
|
|||
```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)
|
||||
}
|
||||
|
|
@ -66,9 +61,9 @@ func add(a: i32, b: i32) => i32 {
|
|||
运行并输出结果:
|
||||
|
||||
```
|
||||
$ go run main.go hello.wa
|
||||
$ wa run hello.wa
|
||||
你好,凹语言!
|
||||
42
|
||||
42 2023
|
||||
2
|
||||
```
|
||||
|
||||
|
|
@ -81,7 +76,7 @@ $ go run main.go hello.wa
|
|||
|
||||
func main {
|
||||
for n := 2; n <= 30; n = n + 1 {
|
||||
var isPrime int = 1
|
||||
isPrime: int = 1
|
||||
for i := 2; i*i <= n; i = i + 1 {
|
||||
if x := n % i; x == 0 {
|
||||
isPrime = 0
|
||||
|
|
@ -97,7 +92,7 @@ func main {
|
|||
运行并输出结果:
|
||||
|
||||
```
|
||||
$ go run main.go run _examples/prime
|
||||
$ cd waroot && wa run examples/prime
|
||||
2
|
||||
3
|
||||
5
|
||||
|
|
@ -134,5 +129,23 @@ $ go run main.go run _examples/prime
|
|||
|
||||
运行的结果和英文语法的示例相同。
|
||||
|
||||
更多例子 [_examples](_examples)
|
||||
更多例子 [waroot/examples](waroot/examples)
|
||||
|
||||
## 贡献者名单
|
||||
|
||||
|贡献者|贡献点|
|
||||
| --- | --- |
|
||||
|柴树杉| 35000|
|
||||
|丁尔男| 42500|
|
||||
|史斌 | 29000|
|
||||
|扈梦明| 14000|
|
||||
|赵普明| 17000|
|
||||
|宋汝阳| 2000|
|
||||
|刘云峰| 1000|
|
||||
|王湘南| 1000|
|
||||
|王泽龙| 1000|
|
||||
|吴烜 | 3000|
|
||||
|刘斌 | 2500|
|
||||
|尹贻浩| 2000|
|
||||
|
||||
贡献点变更记录见 [waroot/cplog](waroot/cplog) 目录。
|
||||
|
|
|
|||
51
README.md
|
|
@ -8,10 +8,8 @@
|
|||
<div align="center">
|
||||
|
||||
[](https://github.com/wa-lang/wa/actions/workflows/wa.yml)
|
||||
[](https://goreportcard.com/report/github.com/wa-lang/wa)
|
||||
[](https://coveralls.io/github/wa-lang/wa)
|
||||
[](https://github.com/wa-lang/wa/releases)
|
||||
[](https://pkg.go.dev/wa-lang.org/wa)
|
||||
[](https://github.com/wa-lang/wa/blob/master/LICENSE)
|
||||
|
||||
</div>
|
||||
|
|
@ -21,7 +19,8 @@ Instead of requiring complex toolchains to set up, you can simply go install it
|
|||
|
||||

|
||||
|
||||
- Home: [https://wa-lang.org](https://wa-lang.org)
|
||||
- Home: [https://wa-lang.github.io/](https://wa-lang.github.io/)
|
||||
- Manual: [https://wa-lang.github.io/man/en/](https://wa-lang.github.io/man/en/)
|
||||
- Github: [https://github.com/wa-lang/wa](https://github.com/wa-lang/wa)
|
||||
- Playground: [https://wa-lang.org/playground](https://wa-lang.org/playground)
|
||||
|
||||
|
|
@ -36,32 +35,22 @@ Instead of requiring complex toolchains to set up, you can simply go install it
|
|||
## Snake Game
|
||||
|
||||
- Play: [https://wa-lang.org/wa/snake/](https://wa-lang.org/wa/snake/)
|
||||
- Code: [_examples/snake/README-en.md](_examples/snake/README-en.md)
|
||||
- Code: [waroot/examples/snake/README-en.md](waroot/examples/snake/README-en.md)
|
||||
|
||||

|
||||
|
||||
## Install and Run:
|
||||
|
||||
Go >= 1.17
|
||||
|
||||
1. `go install wa-lang.org/wa@latest`
|
||||
2. `wa init -name=_examples/hi`
|
||||
3. `wa run _examples/hi`
|
||||
|
||||
> The Wa project is still in very early stage. If you want to submit PR, please read the [Contribution Guide(Chinese)](https://wa-lang.org/community/contribute.html). We do not accept PR only about 3rdparty changes.
|
||||
|
||||
## Example: Print Wa
|
||||
|
||||
Print rune and call function:
|
||||
|
||||
```wa
|
||||
// Copyright @2019-2022 The Wa author. All rights reserved.
|
||||
|
||||
import "fmt"
|
||||
|
||||
global year: i32 = 2023
|
||||
|
||||
func main {
|
||||
println("hello, Wa!")
|
||||
println(add(40, 2))
|
||||
println(add(40, 2), year)
|
||||
|
||||
fmt.Println(1+1)
|
||||
}
|
||||
|
|
@ -74,9 +63,9 @@ func add(a: i32, b: i32) => i32 {
|
|||
Execute the program:
|
||||
|
||||
```
|
||||
$ go run main.go hello.wa
|
||||
$ wa run hello.wa
|
||||
hello, Wa!
|
||||
42
|
||||
42 2023
|
||||
2
|
||||
```
|
||||
|
||||
|
|
@ -84,10 +73,10 @@ hello, Wa!
|
|||
|
||||
Print prime numbers up to 30:
|
||||
|
||||
```
|
||||
```wa
|
||||
func main {
|
||||
for n := 2; n <= 30; n = n + 1 {
|
||||
var isPrime int = 1
|
||||
isPrime: int = 1
|
||||
for i := 2; i*i <= n; i = i + 1 {
|
||||
if x := n % i; x == 0 {
|
||||
isPrime = 0
|
||||
|
|
@ -103,7 +92,7 @@ func main {
|
|||
Execute the program:
|
||||
|
||||
```
|
||||
$ go run main.go run _examples/prime
|
||||
$ cd waroot && wa run examples/prime
|
||||
2
|
||||
3
|
||||
5
|
||||
|
|
@ -142,5 +131,21 @@ Print prime numbers up to 30:
|
|||
|
||||
Output is the same as the previous example.
|
||||
|
||||
More examples [_examples](_examples)
|
||||
More examples [waroot/examples](waroot/examples)
|
||||
|
||||
## Contributors
|
||||
|
||||
|Contributor|Contribution points|
|
||||
| --- | --- |
|
||||
|柴树杉| 35000|
|
||||
|丁尔男| 42500|
|
||||
|史斌 | 29000|
|
||||
|扈梦明| 14000|
|
||||
|赵普明| 17000|
|
||||
|宋汝阳| 2000|
|
||||
|刘云峰| 1000|
|
||||
|王湘南| 1000|
|
||||
|王泽龙| 1000|
|
||||
|吴烜 | 3000|
|
||||
|刘斌 | 2500|
|
||||
|尹贻浩| 2000|
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
dev:
|
||||
go run . -dbg=a1.out.dbg -wat=a1.out.wat
|
||||
|
||||
-rm wa.wasm
|
||||
GOOS=js GOARCH=wasm go build -o wa.wasm
|
||||
node run-go-wasm.js wa.wasm -dbg=a2.out.dbg -wat=a2.out.wat
|
||||
|
||||
clean:
|
||||
-rm *.out.* *.wasm
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
// 版权 @2022 凹语言 作者。保留所有权利。
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"wa-lang.org/wa/api"
|
||||
"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/logger"
|
||||
)
|
||||
|
||||
var waError error
|
||||
|
||||
func waGetError() error {
|
||||
return waError
|
||||
}
|
||||
func waGetErrorText() string {
|
||||
if waError != nil {
|
||||
return waError.Error()
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
func waSetError(err error) {
|
||||
waError = err
|
||||
}
|
||||
func waClearError() {
|
||||
waError = nil
|
||||
}
|
||||
|
||||
func waDebugString(filename, code string) string {
|
||||
cfg := api.DefaultConfig()
|
||||
cfg.WaArch = api.WaArch_wasm
|
||||
cfg.WaOS = api.WaOS_chrome
|
||||
|
||||
prog, err := loader.LoadProgramFile(cfg, filename, code)
|
||||
if err != nil || prog == nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return prog.DebugString()
|
||||
}
|
||||
|
||||
func waGenerateWat(filename, code string) string {
|
||||
cfg := api.DefaultConfig()
|
||||
cfg.WaArch = api.WaArch_wasm
|
||||
cfg.WaOS = api.WaOS_chrome
|
||||
|
||||
wat, err := waBuildFile(cfg, filename, code)
|
||||
if err != nil {
|
||||
if waGetError() == nil {
|
||||
waSetError(err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return string(wat)
|
||||
}
|
||||
|
||||
func waFormatCode(filename, code string) string {
|
||||
newCode, err := api.FormatCode(filename, code)
|
||||
if err != nil {
|
||||
if waGetError() == nil {
|
||||
waSetError(err)
|
||||
}
|
||||
return code
|
||||
}
|
||||
return newCode
|
||||
}
|
||||
|
||||
// 构建 wat 目标
|
||||
func waBuildFile(cfg *config.Config, filename string, src interface{}) (wat []byte, err error) {
|
||||
prog, err := loader.LoadProgramFile(cfg, filename, src)
|
||||
if err != nil || prog == nil {
|
||||
logger.Tracef(&config.EnableTrace_api, "LoadProgramVFS failed, err = %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watOut, err := compiler_wat.New().Compile(prog, "main")
|
||||
return []byte(watOut), err
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
// 版权 @2022 凹语言 作者。保留所有权利。
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
flagDbgOut = flag.String("dbg", "a.out.dbg", "debug output file")
|
||||
flagWatOut = flag.String("wat", "a.out.wat", "wat output file")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
os.Remove(*flagDbgOut)
|
||||
os.Remove(*flagWatOut)
|
||||
|
||||
fmt.Println("=== WARN: only for build wa.wasm === ")
|
||||
|
||||
waClearError()
|
||||
|
||||
outDbg := waDebugString(waName, waCode)
|
||||
outWat := waGenerateWat(waName, waCode)
|
||||
|
||||
if serr := waGetErrorText(); serr != "" {
|
||||
fmt.Println("ERR:", serr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.WriteFile(*flagDbgOut, []byte(outDbg), 0666)
|
||||
os.WriteFile(*flagWatOut, []byte(outWat), 0666)
|
||||
|
||||
fmt.Println("Done")
|
||||
}
|
||||
|
||||
const waName = "hello.wa"
|
||||
const waCode = `// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
import "fmt"
|
||||
import "runtime"
|
||||
|
||||
func main {
|
||||
println("你好,凹语言!", runtime.WAOS)
|
||||
println(add(40, 2))
|
||||
|
||||
fmt.Println(1+1)
|
||||
}
|
||||
|
||||
func add(a: i32, b: i32) => i32 {
|
||||
return a+b
|
||||
}
|
||||
`
|
||||
|
|
@ -1,638 +0,0 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Go1.17.13
|
||||
|
||||
(() => {
|
||||
// Map multiple JavaScript environments to a single common API,
|
||||
// preferring web standards over Node.js API.
|
||||
//
|
||||
// Environments considered:
|
||||
// - Browsers
|
||||
// - Node.js
|
||||
// - Electron
|
||||
// - Parcel
|
||||
// - Webpack
|
||||
|
||||
if (typeof global !== "undefined") {
|
||||
// global already exists
|
||||
} else if (typeof window !== "undefined") {
|
||||
window.global = window;
|
||||
} else if (typeof self !== "undefined") {
|
||||
self.global = self;
|
||||
} else {
|
||||
throw new Error("cannot export Go (neither global, window nor self is defined)");
|
||||
}
|
||||
|
||||
if (!global.require && typeof require !== "undefined") {
|
||||
global.require = require;
|
||||
}
|
||||
|
||||
if (!global.fs && global.require) {
|
||||
const fs = require("fs");
|
||||
if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
|
||||
global.fs = fs;
|
||||
}
|
||||
}
|
||||
|
||||
const enosys = () => {
|
||||
const err = new Error("not implemented");
|
||||
err.code = "ENOSYS";
|
||||
return err;
|
||||
};
|
||||
|
||||
if (!global.fs) {
|
||||
let outputBuf = "";
|
||||
global.fs = {
|
||||
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
|
||||
writeSync(fd, buf) {
|
||||
outputBuf += decoder.decode(buf);
|
||||
const nl = outputBuf.lastIndexOf("\n");
|
||||
if (nl != -1) {
|
||||
console.log(outputBuf.substr(0, nl));
|
||||
outputBuf = outputBuf.substr(nl + 1);
|
||||
}
|
||||
return buf.length;
|
||||
},
|
||||
write(fd, buf, offset, length, position, callback) {
|
||||
if (offset !== 0 || length !== buf.length || position !== null) {
|
||||
callback(enosys());
|
||||
return;
|
||||
}
|
||||
const n = this.writeSync(fd, buf);
|
||||
callback(null, n);
|
||||
},
|
||||
chmod(path, mode, callback) { callback(enosys()); },
|
||||
chown(path, uid, gid, callback) { callback(enosys()); },
|
||||
close(fd, callback) { callback(enosys()); },
|
||||
fchmod(fd, mode, callback) { callback(enosys()); },
|
||||
fchown(fd, uid, gid, callback) { callback(enosys()); },
|
||||
fstat(fd, callback) { callback(enosys()); },
|
||||
fsync(fd, callback) { callback(null); },
|
||||
ftruncate(fd, length, callback) { callback(enosys()); },
|
||||
lchown(path, uid, gid, callback) { callback(enosys()); },
|
||||
link(path, link, callback) { callback(enosys()); },
|
||||
lstat(path, callback) { callback(enosys()); },
|
||||
mkdir(path, perm, callback) { callback(enosys()); },
|
||||
open(path, flags, mode, callback) { callback(enosys()); },
|
||||
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
|
||||
readdir(path, callback) { callback(enosys()); },
|
||||
readlink(path, callback) { callback(enosys()); },
|
||||
rename(from, to, callback) { callback(enosys()); },
|
||||
rmdir(path, callback) { callback(enosys()); },
|
||||
stat(path, callback) { callback(enosys()); },
|
||||
symlink(path, link, callback) { callback(enosys()); },
|
||||
truncate(path, length, callback) { callback(enosys()); },
|
||||
unlink(path, callback) { callback(enosys()); },
|
||||
utimes(path, atime, mtime, callback) { callback(enosys()); },
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.process) {
|
||||
global.process = {
|
||||
getuid() { return -1; },
|
||||
getgid() { return -1; },
|
||||
geteuid() { return -1; },
|
||||
getegid() { return -1; },
|
||||
getgroups() { throw enosys(); },
|
||||
pid: -1,
|
||||
ppid: -1,
|
||||
umask() { throw enosys(); },
|
||||
cwd() { throw enosys(); },
|
||||
chdir() { throw enosys(); },
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.crypto && global.require) {
|
||||
const nodeCrypto = require("crypto");
|
||||
global.crypto = {
|
||||
getRandomValues(b) {
|
||||
nodeCrypto.randomFillSync(b);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!global.crypto) {
|
||||
throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
|
||||
}
|
||||
|
||||
if (!global.performance) {
|
||||
global.performance = {
|
||||
now() {
|
||||
const [sec, nsec] = process.hrtime();
|
||||
return sec * 1000 + nsec / 1000000;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.TextEncoder && global.require) {
|
||||
global.TextEncoder = require("util").TextEncoder;
|
||||
}
|
||||
if (!global.TextEncoder) {
|
||||
throw new Error("global.TextEncoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
if (!global.TextDecoder && global.require) {
|
||||
global.TextDecoder = require("util").TextDecoder;
|
||||
}
|
||||
if (!global.TextDecoder) {
|
||||
throw new Error("global.TextDecoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
// End of polyfills for common API.
|
||||
|
||||
const encoder = new TextEncoder("utf-8");
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
global.Go = class {
|
||||
constructor() {
|
||||
this.argv = ["js"];
|
||||
this.env = {};
|
||||
this.exit = (code) => {
|
||||
if (code !== 0) {
|
||||
console.warn("exit code:", code);
|
||||
}
|
||||
};
|
||||
this._exitPromise = new Promise((resolve) => {
|
||||
this._resolveExitPromise = resolve;
|
||||
});
|
||||
this._pendingEvent = null;
|
||||
this._scheduledTimeouts = new Map();
|
||||
this._nextCallbackTimeoutID = 1;
|
||||
|
||||
const setInt64 = (addr, v) => {
|
||||
this.mem.setUint32(addr + 0, v, true);
|
||||
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
||||
}
|
||||
|
||||
const getInt64 = (addr) => {
|
||||
const low = this.mem.getUint32(addr + 0, true);
|
||||
const high = this.mem.getInt32(addr + 4, true);
|
||||
return low + high * 4294967296;
|
||||
}
|
||||
|
||||
const loadValue = (addr) => {
|
||||
const f = this.mem.getFloat64(addr, true);
|
||||
if (f === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
const id = this.mem.getUint32(addr, true);
|
||||
return this._values[id];
|
||||
}
|
||||
|
||||
const storeValue = (addr, v) => {
|
||||
const nanHead = 0x7FF80000;
|
||||
|
||||
if (typeof v === "number" && v !== 0) {
|
||||
if (isNaN(v)) {
|
||||
this.mem.setUint32(addr + 4, nanHead, true);
|
||||
this.mem.setUint32(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
this.mem.setFloat64(addr, v, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v === undefined) {
|
||||
this.mem.setFloat64(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
let id = this._ids.get(v);
|
||||
if (id === undefined) {
|
||||
id = this._idPool.pop();
|
||||
if (id === undefined) {
|
||||
id = this._values.length;
|
||||
}
|
||||
this._values[id] = v;
|
||||
this._goRefCounts[id] = 0;
|
||||
this._ids.set(v, id);
|
||||
}
|
||||
this._goRefCounts[id]++;
|
||||
let typeFlag = 0;
|
||||
switch (typeof v) {
|
||||
case "object":
|
||||
if (v !== null) {
|
||||
typeFlag = 1;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
typeFlag = 2;
|
||||
break;
|
||||
case "symbol":
|
||||
typeFlag = 3;
|
||||
break;
|
||||
case "function":
|
||||
typeFlag = 4;
|
||||
break;
|
||||
}
|
||||
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
||||
this.mem.setUint32(addr, id, true);
|
||||
}
|
||||
|
||||
const loadSlice = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
||||
}
|
||||
|
||||
const loadSliceOfValues = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
const a = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
a[i] = loadValue(array + i * 8);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
const loadString = (addr) => {
|
||||
const saddr = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
||||
}
|
||||
|
||||
const timeOrigin = Date.now() - performance.now();
|
||||
this.importObject = {
|
||||
go: {
|
||||
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
||||
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
||||
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
||||
// This changes the SP, thus we have to update the SP used by the imported function.
|
||||
|
||||
// func wasmExit(code int32)
|
||||
"runtime.wasmExit": (sp) => {
|
||||
sp >>>= 0;
|
||||
const code = this.mem.getInt32(sp + 8, true);
|
||||
this.exited = true;
|
||||
delete this._inst;
|
||||
delete this._values;
|
||||
delete this._goRefCounts;
|
||||
delete this._ids;
|
||||
delete this._idPool;
|
||||
this.exit(code);
|
||||
},
|
||||
|
||||
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
||||
"runtime.wasmWrite": (sp) => {
|
||||
sp >>>= 0;
|
||||
const fd = getInt64(sp + 8);
|
||||
const p = getInt64(sp + 16);
|
||||
const n = this.mem.getInt32(sp + 24, true);
|
||||
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
||||
},
|
||||
|
||||
// func resetMemoryDataView()
|
||||
"runtime.resetMemoryDataView": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
},
|
||||
|
||||
// func nanotime1() int64
|
||||
"runtime.nanotime1": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
||||
},
|
||||
|
||||
// func walltime() (sec int64, nsec int32)
|
||||
"runtime.walltime": (sp) => {
|
||||
sp >>>= 0;
|
||||
const msec = (new Date).getTime();
|
||||
setInt64(sp + 8, msec / 1000);
|
||||
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
||||
},
|
||||
|
||||
// func scheduleTimeoutEvent(delay int64) int32
|
||||
"runtime.scheduleTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this._nextCallbackTimeoutID;
|
||||
this._nextCallbackTimeoutID++;
|
||||
this._scheduledTimeouts.set(id, setTimeout(
|
||||
() => {
|
||||
this._resume();
|
||||
while (this._scheduledTimeouts.has(id)) {
|
||||
// for some reason Go failed to register the timeout event, log and try again
|
||||
// (temporary workaround for https://github.com/golang/go/issues/28975)
|
||||
console.warn("scheduleTimeoutEvent: missed timeout event");
|
||||
this._resume();
|
||||
}
|
||||
},
|
||||
getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
|
||||
));
|
||||
this.mem.setInt32(sp + 16, id, true);
|
||||
},
|
||||
|
||||
// func clearTimeoutEvent(id int32)
|
||||
"runtime.clearTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getInt32(sp + 8, true);
|
||||
clearTimeout(this._scheduledTimeouts.get(id));
|
||||
this._scheduledTimeouts.delete(id);
|
||||
},
|
||||
|
||||
// func getRandomData(r []byte)
|
||||
"runtime.getRandomData": (sp) => {
|
||||
sp >>>= 0;
|
||||
crypto.getRandomValues(loadSlice(sp + 8));
|
||||
},
|
||||
|
||||
// func finalizeRef(v ref)
|
||||
"syscall/js.finalizeRef": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getUint32(sp + 8, true);
|
||||
this._goRefCounts[id]--;
|
||||
if (this._goRefCounts[id] === 0) {
|
||||
const v = this._values[id];
|
||||
this._values[id] = null;
|
||||
this._ids.delete(v);
|
||||
this._idPool.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
// func stringVal(value string) ref
|
||||
"syscall/js.stringVal": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, loadString(sp + 8));
|
||||
},
|
||||
|
||||
// func valueGet(v ref, p string) ref
|
||||
"syscall/js.valueGet": (sp) => {
|
||||
sp >>>= 0;
|
||||
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 32, result);
|
||||
},
|
||||
|
||||
// func valueSet(v ref, p string, x ref)
|
||||
"syscall/js.valueSet": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
||||
},
|
||||
|
||||
// func valueDelete(v ref, p string)
|
||||
"syscall/js.valueDelete": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
||||
},
|
||||
|
||||
// func valueIndex(v ref, i int) ref
|
||||
"syscall/js.valueIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
||||
},
|
||||
|
||||
// valueSetIndex(v ref, i int, x ref)
|
||||
"syscall/js.valueSetIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
||||
},
|
||||
|
||||
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||
"syscall/js.valueCall": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const m = Reflect.get(v, loadString(sp + 16));
|
||||
const args = loadSliceOfValues(sp + 32);
|
||||
const result = Reflect.apply(m, v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 56, result);
|
||||
this.mem.setUint8(sp + 64, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 56, err);
|
||||
this.mem.setUint8(sp + 64, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueInvoke(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueInvoke": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.apply(v, undefined, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueNew(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueNew": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.construct(v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueLength(v ref) int
|
||||
"syscall/js.valueLength": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
||||
},
|
||||
|
||||
// valuePrepareString(v ref) (ref, int)
|
||||
"syscall/js.valuePrepareString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = encoder.encode(String(loadValue(sp + 8)));
|
||||
storeValue(sp + 16, str);
|
||||
setInt64(sp + 24, str.length);
|
||||
},
|
||||
|
||||
// valueLoadString(v ref, b []byte)
|
||||
"syscall/js.valueLoadString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = loadValue(sp + 8);
|
||||
loadSlice(sp + 16).set(str);
|
||||
},
|
||||
|
||||
// func valueInstanceOf(v ref, t ref) bool
|
||||
"syscall/js.valueInstanceOf": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
||||
},
|
||||
|
||||
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||
"syscall/js.copyBytesToGo": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadSlice(sp + 8);
|
||||
const src = loadValue(sp + 32);
|
||||
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
// func copyBytesToJS(dst ref, src []byte) (int, bool)
|
||||
"syscall/js.copyBytesToJS": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadValue(sp + 8);
|
||||
const src = loadSlice(sp + 16);
|
||||
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
"debug": (value) => {
|
||||
console.log(value);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async run(instance) {
|
||||
if (!(instance instanceof WebAssembly.Instance)) {
|
||||
throw new Error("Go.run: WebAssembly.Instance expected");
|
||||
}
|
||||
this._inst = instance;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
||||
NaN,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
global,
|
||||
this,
|
||||
];
|
||||
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
||||
this._ids = new Map([ // mapping from JS values to reference ids
|
||||
[0, 1],
|
||||
[null, 2],
|
||||
[true, 3],
|
||||
[false, 4],
|
||||
[global, 5],
|
||||
[this, 6],
|
||||
]);
|
||||
this._idPool = []; // unused ids that have been garbage collected
|
||||
this.exited = false; // whether the Go program has exited
|
||||
|
||||
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
||||
let offset = 4096;
|
||||
|
||||
const strPtr = (str) => {
|
||||
const ptr = offset;
|
||||
const bytes = encoder.encode(str + "\0");
|
||||
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
||||
offset += bytes.length;
|
||||
if (offset % 8 !== 0) {
|
||||
offset += 8 - (offset % 8);
|
||||
}
|
||||
return ptr;
|
||||
};
|
||||
|
||||
const argc = this.argv.length;
|
||||
|
||||
const argvPtrs = [];
|
||||
this.argv.forEach((arg) => {
|
||||
argvPtrs.push(strPtr(arg));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const keys = Object.keys(this.env).sort();
|
||||
keys.forEach((key) => {
|
||||
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const argv = offset;
|
||||
argvPtrs.forEach((ptr) => {
|
||||
this.mem.setUint32(offset, ptr, true);
|
||||
this.mem.setUint32(offset + 4, 0, true);
|
||||
offset += 8;
|
||||
});
|
||||
|
||||
// The linker guarantees global data starts from at least wasmMinDataAddr.
|
||||
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
|
||||
const wasmMinDataAddr = 4096 + 8192;
|
||||
if (offset >= wasmMinDataAddr) {
|
||||
throw new Error("total length of command line and environment variables exceeds limit");
|
||||
}
|
||||
|
||||
this._inst.exports.run(argc, argv);
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
await this._exitPromise;
|
||||
}
|
||||
|
||||
_resume() {
|
||||
if (this.exited) {
|
||||
throw new Error("Go program has already exited");
|
||||
}
|
||||
this._inst.exports.resume();
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
}
|
||||
|
||||
_makeFuncWrapper(id) {
|
||||
const go = this;
|
||||
return function () {
|
||||
const event = { id: id, this: this, args: arguments };
|
||||
go._pendingEvent = event;
|
||||
go._resume();
|
||||
return event.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof module !== "undefined" &&
|
||||
global.require &&
|
||||
global.require.main === module &&
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
!global.process.versions.electron
|
||||
) {
|
||||
if (process.argv.length < 3) {
|
||||
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
go.argv = process.argv.slice(2);
|
||||
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
|
||||
go.exit = process.exit;
|
||||
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
|
||||
process.on("exit", (code) => { // Node.js exits if no event handler is pending
|
||||
if (code === 0 && !go.exited) {
|
||||
// deadlock, make Go print error and stack traces
|
||||
go._pendingEvent = { id: 0 };
|
||||
go._resume();
|
||||
}
|
||||
});
|
||||
return go.run(result.instance);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
default:
|
||||
|
||||
ci-test-all:
|
||||
@echo "== _examples test begin =="
|
||||
|
||||
# loop forever
|
||||
# cd ./arduino && make
|
||||
# cd ./arduino-wat && make
|
||||
|
||||
cd ./brainfuck && make
|
||||
cd ./expr && make
|
||||
cd ./hello && make
|
||||
cd ./pkg && make
|
||||
cd ./prime && make
|
||||
cd ./reftoptr && make
|
||||
|
||||
# snake
|
||||
cd ./snake && make publish
|
||||
|
||||
go run ../main.go interface_named.wa
|
||||
go run ../main.go struct.wa
|
||||
|
||||
@echo "== _examples ok =="
|
||||
|
||||
clean:
|
||||
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
unsigned char app_wasm[] = {
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x2a, 0x08, 0x60,
|
||||
0x01, 0x7f, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x01, 0x7f,
|
||||
0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01,
|
||||
0x7f, 0x60, 0x01, 0x7f, 0x02, 0x7f, 0x7f, 0x60, 0x00, 0x00, 0x60, 0x03,
|
||||
0x7f, 0x7f, 0x7f, 0x00, 0x02, 0x6f, 0x06, 0x07, 0x61, 0x72, 0x64, 0x75,
|
||||
0x69, 0x6e, 0x6f, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x00, 0x00, 0x07,
|
||||
0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x07, 0x70, 0x69, 0x6e, 0x4d,
|
||||
0x6f, 0x64, 0x65, 0x00, 0x01, 0x07, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
|
||||
0x6f, 0x0c, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x57, 0x72, 0x69,
|
||||
0x74, 0x65, 0x00, 0x01, 0x07, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f,
|
||||
0x06, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x00, 0x02, 0x07, 0x61, 0x72,
|
||||
0x64, 0x75, 0x69, 0x6e, 0x6f, 0x09, 0x67, 0x65, 0x74, 0x50, 0x69, 0x6e,
|
||||
0x4c, 0x45, 0x44, 0x00, 0x02, 0x07, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e,
|
||||
0x6f, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x01, 0x03, 0x13, 0x12,
|
||||
0x03, 0x00, 0x04, 0x05, 0x03, 0x00, 0x06, 0x06, 0x00, 0x00, 0x01, 0x06,
|
||||
0x06, 0x06, 0x06, 0x07, 0x07, 0x06, 0x04, 0x04, 0x01, 0x70, 0x00, 0x01,
|
||||
0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x43, 0x0d, 0x7f, 0x01, 0x41, 0x80,
|
||||
0x10, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b,
|
||||
0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01,
|
||||
0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00,
|
||||
0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f,
|
||||
0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41, 0x00, 0x0b, 0x7f, 0x01, 0x41,
|
||||
0x00, 0x0b, 0x07, 0xdc, 0x01, 0x0c, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
|
||||
0x79, 0x02, 0x00, 0x0c, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e,
|
||||
0x69, 0x6e, 0x69, 0x74, 0x00, 0x0d, 0x13, 0x24, 0x72, 0x75, 0x6e, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x2e, 0x77, 0x61, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49,
|
||||
0x33, 0x32, 0x00, 0x0e, 0x14, 0x24, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x2e, 0x77, 0x61, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x52, 0x75, 0x6e,
|
||||
0x65, 0x00, 0x0f, 0x0f, 0x24, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x2e, 0x77, 0x61, 0x50, 0x75, 0x74, 0x73, 0x00, 0x10, 0x0f, 0x5f, 0x5f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x5f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x23,
|
||||
0x31, 0x00, 0x11, 0x0d, 0x5f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x5f,
|
||||
0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x12, 0x0d, 0x5f, 0x5f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x5f, 0x5f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x13, 0x14,
|
||||
0x73, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x24, 0x61, 0x72, 0x64, 0x75,
|
||||
0x69, 0x6e, 0x6f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x14, 0x17, 0x73,
|
||||
0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x24, 0x61, 0x72, 0x64, 0x75, 0x69,
|
||||
0x6e, 0x6f, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x6c, 0x6e, 0x00, 0x15,
|
||||
0x15, 0x73, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x24, 0x61, 0x72, 0x64,
|
||||
0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x16,
|
||||
0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x17, 0x0a, 0xcb, 0x07,
|
||||
0x12, 0x0b, 0x00, 0x23, 0x00, 0x23, 0x00, 0x20, 0x00, 0x6a, 0x24, 0x00,
|
||||
0x0b, 0x02, 0x00, 0x0b, 0x25, 0x00, 0x20, 0x00, 0x20, 0x00, 0x04, 0x40,
|
||||
0x20, 0x00, 0x41, 0x01, 0x36, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36,
|
||||
0x00, 0x04, 0x20, 0x00, 0x20, 0x02, 0x36, 0x00, 0x08, 0x20, 0x00, 0x20,
|
||||
0x03, 0x36, 0x00, 0x0c, 0x0b, 0x0b, 0x06, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||
0x0b, 0x16, 0x00, 0x20, 0x00, 0x20, 0x00, 0x04, 0x40, 0x20, 0x00, 0x20,
|
||||
0x00, 0x28, 0x00, 0x00, 0x41, 0x01, 0x6a, 0x36, 0x00, 0x00, 0x0b, 0x0b,
|
||||
0x6d, 0x01, 0x05, 0x7f, 0x20, 0x00, 0x41, 0x00, 0x46, 0x04, 0x40, 0x0f,
|
||||
0x0b, 0x20, 0x00, 0x28, 0x00, 0x00, 0x41, 0x01, 0x6b, 0x21, 0x01, 0x20,
|
||||
0x01, 0x04, 0x40, 0x20, 0x00, 0x20, 0x01, 0x36, 0x00, 0x00, 0x05, 0x20,
|
||||
0x00, 0x28, 0x00, 0x08, 0x21, 0x03, 0x20, 0x03, 0x04, 0x40, 0x20, 0x00,
|
||||
0x28, 0x00, 0x04, 0x21, 0x02, 0x20, 0x02, 0x04, 0x40, 0x20, 0x00, 0x28,
|
||||
0x00, 0x0c, 0x21, 0x04, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x03,
|
||||
0x40, 0x20, 0x05, 0x20, 0x03, 0x11, 0x00, 0x00, 0x20, 0x02, 0x41, 0x01,
|
||||
0x6b, 0x21, 0x02, 0x20, 0x02, 0x04, 0x40, 0x20, 0x05, 0x20, 0x04, 0x6a,
|
||||
0x21, 0x05, 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x0b, 0x20, 0x00, 0x10, 0x07,
|
||||
0x0b, 0x0b, 0x2c, 0x00, 0x41, 0x04, 0x10, 0x06, 0x24, 0x03, 0x41, 0x04,
|
||||
0x10, 0x06, 0x24, 0x04, 0x41, 0x04, 0x10, 0x06, 0x24, 0x05, 0x41, 0x04,
|
||||
0x10, 0x06, 0x24, 0x07, 0x41, 0x04, 0x10, 0x06, 0x24, 0x08, 0x41, 0x0c,
|
||||
0x10, 0x06, 0x24, 0x09, 0x41, 0x04, 0x10, 0x06, 0x24, 0x0b, 0x0b, 0x6c,
|
||||
0x01, 0x03, 0x7f, 0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x01, 0x41,
|
||||
0x00, 0x21, 0x02, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
|
||||
0x40, 0x02, 0x40, 0x20, 0x00, 0x0e, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0b,
|
||||
0x41, 0x00, 0x21, 0x01, 0x23, 0x0a, 0x21, 0x02, 0x20, 0x02, 0x04, 0x40,
|
||||
0x0c, 0x02, 0x05, 0x0c, 0x01, 0x0b, 0x0b, 0x41, 0x01, 0x21, 0x01, 0x41,
|
||||
0x01, 0x24, 0x0a, 0x10, 0x14, 0x23, 0x09, 0x41, 0x00, 0x10, 0x0a, 0x23,
|
||||
0x09, 0x28, 0x00, 0x00, 0x10, 0x0b, 0x36, 0x00, 0x00, 0x23, 0x09, 0x41,
|
||||
0x08, 0x36, 0x00, 0x04, 0x23, 0x09, 0x41, 0x07, 0x36, 0x00, 0x08, 0x0c,
|
||||
0x00, 0x0b, 0x41, 0x02, 0x21, 0x01, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x24, 0x01, 0x02, 0x7f, 0x41, 0x00, 0x21, 0x01, 0x41, 0x00, 0x21, 0x02,
|
||||
0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x0e, 0x01,
|
||||
0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x24, 0x01, 0x02, 0x7f, 0x41, 0x00, 0x21, 0x01, 0x41, 0x00, 0x21,
|
||||
0x02, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x0e,
|
||||
0x01, 0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x2a, 0x01, 0x02, 0x7f, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00,
|
||||
0x21, 0x03, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02,
|
||||
0x0e, 0x01, 0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x03, 0x20, 0x00, 0x20,
|
||||
0x01, 0x10, 0x05, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x0b, 0x49, 0x01, 0x03,
|
||||
0x7f, 0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x01, 0x41, 0x00, 0x21,
|
||||
0x02, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x0e,
|
||||
0x01, 0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x01, 0x23, 0x0b, 0x28, 0x00,
|
||||
0x00, 0x21, 0x02, 0x20, 0x02, 0x41, 0x01, 0x10, 0x01, 0x41, 0x00, 0x10,
|
||||
0x0a, 0x41, 0x0f, 0x41, 0x25, 0x10, 0x16, 0x41, 0x00, 0x10, 0x0a, 0x41,
|
||||
0x34, 0x41, 0x14, 0x10, 0x16, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x0b, 0x74,
|
||||
0x01, 0x06, 0x7f, 0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x01, 0x41,
|
||||
0x00, 0x21, 0x02, 0x41, 0x00, 0x21, 0x03, 0x41, 0x00, 0x21, 0x04, 0x41,
|
||||
0x00, 0x21, 0x05, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
|
||||
0x40, 0x20, 0x00, 0x0e, 0x02, 0x00, 0x01, 0x00, 0x0b, 0x41, 0x00, 0x21,
|
||||
0x01, 0x0c, 0x00, 0x0b, 0x41, 0x01, 0x21, 0x01, 0x23, 0x0b, 0x28, 0x00,
|
||||
0x00, 0x21, 0x02, 0x23, 0x05, 0x28, 0x00, 0x00, 0x21, 0x03, 0x20, 0x02,
|
||||
0x20, 0x03, 0x10, 0x02, 0x41, 0xe4, 0x00, 0x10, 0x00, 0x23, 0x0b, 0x28,
|
||||
0x00, 0x00, 0x21, 0x04, 0x23, 0x03, 0x28, 0x00, 0x00, 0x21, 0x05, 0x20,
|
||||
0x04, 0x20, 0x05, 0x10, 0x02, 0x41, 0x84, 0x07, 0x10, 0x00, 0x41, 0x01,
|
||||
0x21, 0x00, 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x0b, 0x5f, 0x01, 0x04, 0x7f,
|
||||
0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x01, 0x41, 0x00, 0x21, 0x02,
|
||||
0x41, 0x00, 0x21, 0x03, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40,
|
||||
0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x0e, 0x03, 0x00, 0x01, 0x02, 0x00,
|
||||
0x0b, 0x41, 0x00, 0x21, 0x01, 0x23, 0x0c, 0x21, 0x02, 0x20, 0x02, 0x04,
|
||||
0x40, 0x0c, 0x02, 0x05, 0x0c, 0x01, 0x0b, 0x0b, 0x41, 0x01, 0x21, 0x01,
|
||||
0x41, 0x01, 0x24, 0x0c, 0x10, 0x14, 0x10, 0x04, 0x21, 0x03, 0x23, 0x0b,
|
||||
0x20, 0x03, 0x36, 0x00, 0x00, 0x10, 0x11, 0x0c, 0x00, 0x0b, 0x41, 0x02,
|
||||
0x21, 0x01, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x0b, 0x6f, 0x01, 0x03, 0x7f,
|
||||
0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x01, 0x41, 0x00, 0x21, 0x02,
|
||||
0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
|
||||
0x20, 0x00, 0x0e, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0b, 0x41, 0x00, 0x21,
|
||||
0x01, 0x23, 0x06, 0x21, 0x02, 0x20, 0x02, 0x04, 0x40, 0x0c, 0x02, 0x05,
|
||||
0x0c, 0x01, 0x0b, 0x0b, 0x41, 0x01, 0x21, 0x01, 0x41, 0x01, 0x24, 0x06,
|
||||
0x23, 0x03, 0x41, 0x00, 0x36, 0x00, 0x00, 0x23, 0x05, 0x41, 0x01, 0x36,
|
||||
0x00, 0x00, 0x23, 0x04, 0x41, 0x00, 0x36, 0x00, 0x00, 0x23, 0x08, 0x41,
|
||||
0x01, 0x36, 0x00, 0x00, 0x23, 0x07, 0x41, 0x02, 0x36, 0x00, 0x00, 0x0c,
|
||||
0x00, 0x0b, 0x41, 0x02, 0x21, 0x01, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x32, 0x01, 0x02, 0x7f, 0x41, 0x00, 0x21, 0x03, 0x41, 0x00, 0x21, 0x04,
|
||||
0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x0e, 0x01,
|
||||
0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x20, 0x01, 0x20, 0x02, 0x10,
|
||||
0x10, 0x41, 0x0a, 0x10, 0x0f, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x20, 0x00,
|
||||
0x10, 0x0b, 0x0b, 0x2e, 0x01, 0x02, 0x7f, 0x41, 0x00, 0x21, 0x03, 0x41,
|
||||
0x00, 0x21, 0x04, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20,
|
||||
0x03, 0x0e, 0x01, 0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x20, 0x01,
|
||||
0x20, 0x02, 0x10, 0x10, 0x0c, 0x02, 0x0b, 0x0b, 0x0b, 0x20, 0x00, 0x10,
|
||||
0x0b, 0x0b, 0x08, 0x00, 0x10, 0x0c, 0x10, 0x13, 0x10, 0x12, 0x0b, 0x0b,
|
||||
0x4e, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x48, 0x24, 0x24, 0x77, 0x61, 0x64,
|
||||
0x73, 0x24, 0x24, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0xe5, 0x87,
|
||||
0xb9, 0xe8, 0xaf, 0xad, 0xe8, 0xa8, 0x80, 0x28, 0x57, 0x61, 0x29, 0x2f,
|
||||
0x41, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x20, 0x69, 0x73, 0x20, 0x72,
|
||||
0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x2e, 0x2e, 0x2e, 0x0a, 0x68,
|
||||
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x61, 0x2d, 0x6c, 0x61,
|
||||
0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x0a
|
||||
};
|
||||
unsigned int app_wasm_len = 1543;
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/brainfuck",
|
||||
"pkgpath": "brainfuck",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
default:
|
||||
go run ../../main.go yacc -l -p=expr -c="copyright.txt" -o="y.wa" expr.y
|
||||
go run ../../main.go y.wa
|
||||
|
||||
clean:
|
||||
-rm y.* a.out*
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
:: 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
setlocal
|
||||
|
||||
cd %~dp0
|
||||
|
||||
go run ../../main.go yacc -l -p=expr -c="copyright.txt" -o="y.wa" expr.y
|
||||
go run ../../main.go "y.wa"
|
||||
|
|
@ -1 +0,0 @@
|
|||
版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// 版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
||||
func Println(x: int) {
|
||||
println(x)
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/hello",
|
||||
"pkgpath": "myapp",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
// 版权 @2021 凹语言 作者。保留所有权利。
|
||||
|
||||
type S1 struct {
|
||||
a: i32
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
b: i32
|
||||
}
|
||||
|
||||
type I1 interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type I2 interface {
|
||||
f2()
|
||||
}
|
||||
|
||||
func S1.f() {
|
||||
println("This is S1, this.a==", this.a)
|
||||
}
|
||||
|
||||
func S2.f(){
|
||||
println("This is S2, this.b==", this.b)
|
||||
}
|
||||
|
||||
func main() {
|
||||
v1 := S1{a: 13}
|
||||
|
||||
var i1: I1 = &v1 //具体类型到具名接口
|
||||
v1.f() //直接调用
|
||||
i1.f() //接口调用
|
||||
doConcreteType(i1)
|
||||
i1.f()
|
||||
|
||||
v2 := S2{b: 42}
|
||||
i1 = &v2 //具体类型到具名接口
|
||||
i1.f()
|
||||
doConcreteType(i1)
|
||||
i1.f()
|
||||
|
||||
var ni: interface{} = &v1 //具体类型到空接口
|
||||
i1 = ni.(I1) //接口动态互转
|
||||
i1.f()
|
||||
|
||||
ni = &v2 //具体类型到空接口
|
||||
i1 = ni.(I1) //接口动态互转
|
||||
i1.f()
|
||||
|
||||
var ival: i32 = 777
|
||||
ni = ival
|
||||
doConcreteType(ni)
|
||||
|
||||
doConcreteType(v1)
|
||||
doConcreteType(v2)
|
||||
doConcreteType("你好凹语言")
|
||||
|
||||
//i2 := ni.(I2) //接口互转,由于v2未实现I2,这会触发异常
|
||||
//i2.f2()
|
||||
|
||||
var anoi: interface{ f() } = &v1 //具体类型到匿名接口
|
||||
doConcreteType(anoi) //匿名接口向具名接口转换
|
||||
anoi.f()
|
||||
}
|
||||
|
||||
func doConcreteType(i: interface{}) {
|
||||
//接口到具体类型断言
|
||||
switch c := i.(type){
|
||||
case *S1:
|
||||
println("*S1")
|
||||
c.a *= 2
|
||||
|
||||
case *S2:
|
||||
println("*S2")
|
||||
c.b *= 2
|
||||
|
||||
case i32:
|
||||
println("i32: ", c)
|
||||
|
||||
case string:
|
||||
println("string: ", c)
|
||||
|
||||
case S1:
|
||||
println("S1, S1.a==", c.a)
|
||||
|
||||
case S2:
|
||||
println("S2, S2.b==", c.b)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// 版权 @2022 凹语言 作者。保留所有权利。
|
||||
|
||||
func main {
|
||||
test_array(2, 4)
|
||||
test_array(7, 9)
|
||||
}
|
||||
|
||||
func test_array(a: int, b: int) {
|
||||
var arr [10][10]int
|
||||
for i := 0; i < 10; i++ {
|
||||
for j := 0; j < 10; j++ {
|
||||
arr[i][j] = i * j
|
||||
}
|
||||
}
|
||||
|
||||
println("arr[", a, "][", b, "] = ", arr[a][b])
|
||||
println("arr[", b, "][", a, "] = ", arr[b][a])
|
||||
}
|
||||
|
|
@ -1,428 +0,0 @@
|
|||
// 版权 @2022 凹语言 作者。保留所有权利。
|
||||
|
||||
func main {
|
||||
println("您好,凹语言!\nHello, wa-lang!")
|
||||
println("幸运整数:", test_calc1(6, 9, 8), ", ", 5, ", ", test_calc5(6, 9, 8))
|
||||
println("幸运浮点数:", 5.44, ", ", test_calc3(6, 9, 8))
|
||||
|
||||
test_print(-100, 100, -100, 100, 5)
|
||||
|
||||
test_cmp0(20, 19)
|
||||
test_cmp0(20, 20)
|
||||
test_cmp1(-3, -3)
|
||||
test_cmp1(-3, -4)
|
||||
test_cmp2(10, 10)
|
||||
test_cmp2(10, 10.0001)
|
||||
|
||||
test_jmp(4)
|
||||
test_loop0(10)
|
||||
test_loop1(10, 10)
|
||||
|
||||
test_array(2, 4)
|
||||
test_array(7, 9)
|
||||
|
||||
test_struct0(10)
|
||||
test_struct0(11)
|
||||
test_struct0(2)
|
||||
test_struct0(13)
|
||||
test_struct0(0)
|
||||
|
||||
test_struct1(10, 0, 0)
|
||||
test_struct1(11, 1, 0)
|
||||
test_struct1(2, 0, 1)
|
||||
test_struct1(13, 1, 0)
|
||||
test_struct1(2, 1, 0)
|
||||
|
||||
test_convert0()
|
||||
test_convert1()
|
||||
test_convert2()
|
||||
test_convert3()
|
||||
|
||||
test_global_consts()
|
||||
test_global_variables_0()
|
||||
test_global_variables_1()
|
||||
|
||||
test_pointer(1)
|
||||
test_pointer(-1)
|
||||
|
||||
test_f32()
|
||||
|
||||
test_struct2()
|
||||
test_struct3()
|
||||
test_struct4()
|
||||
|
||||
test_array2()
|
||||
test_array3()
|
||||
test_array4()
|
||||
|
||||
bye()
|
||||
}
|
||||
|
||||
func bye() {
|
||||
println("再见!\nbye!")
|
||||
}
|
||||
|
||||
func test_calc0(a i32, b i32, c i32) i32 {
|
||||
return (-a) * 5 / (b - c + 2)
|
||||
}
|
||||
|
||||
func test_calc1(a i32, b i32, c i32) i32 {
|
||||
return test_calc0(a, b, c)
|
||||
}
|
||||
|
||||
func test_calc2(a f64, b f64, c f64) f64 {
|
||||
return (-a) * 5 / (b - c + 2)
|
||||
}
|
||||
|
||||
func test_calc3(a f64, b f64, c f64) f64 {
|
||||
return test_calc2(a, b, c)
|
||||
}
|
||||
|
||||
func test_calc4(a u16, b u16, c u16) u16 {
|
||||
return (-a) * 5 / (b - c + 2)
|
||||
}
|
||||
|
||||
func test_calc5(a u16, b u16, c u16) u16 {
|
||||
return test_calc4(-a, b, c)
|
||||
}
|
||||
|
||||
func test_cmp0(a u16, b u16) {
|
||||
if a > b {
|
||||
println(a, " > ", b)
|
||||
} else {
|
||||
println(a, " <= ", b)
|
||||
}
|
||||
}
|
||||
|
||||
func test_cmp1(a i64, b i64) {
|
||||
if a <= b {
|
||||
println(a, " <= ", b)
|
||||
} else {
|
||||
println(a, " > ", b)
|
||||
}
|
||||
}
|
||||
|
||||
func test_cmp2(a f64, b f64) {
|
||||
if a != b {
|
||||
println(a, " != ", b)
|
||||
} else {
|
||||
println(a, " == ", b)
|
||||
}
|
||||
}
|
||||
|
||||
func test_jmp(a i32) {
|
||||
for {
|
||||
if a > 10 {
|
||||
return
|
||||
}
|
||||
println(a)
|
||||
a++
|
||||
}
|
||||
}
|
||||
|
||||
func test_loop0(a i16) {
|
||||
for i := i16(0); i < a; i++ {
|
||||
println(i, " * ", i, " = ", i * i)
|
||||
}
|
||||
}
|
||||
|
||||
func test_loop1(a u64, b u64) {
|
||||
for i := u64(0); i < a; i++ {
|
||||
for j := u64(0); j < b; j++ {
|
||||
println(i, " * ", j, " = ", i * j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func test_array(a int, b int) {
|
||||
var arr [10][10]int
|
||||
for i := 0; i < 10; i++ {
|
||||
for j := 0; j < 10; j++ {
|
||||
arr[i][j] = i * j
|
||||
}
|
||||
}
|
||||
|
||||
println("arr[", a, "][", b, "] = ", arr[a][b])
|
||||
println("arr[", b, "][", a, "] = ", arr[b][a])
|
||||
}
|
||||
|
||||
type t0 struct {
|
||||
a int64
|
||||
b int32
|
||||
c [4]f64
|
||||
d uint32
|
||||
}
|
||||
|
||||
func test_struct0(a int) {
|
||||
s0 := t0{17, -668, [4]f64{2.71828, 0.717, 3.14159}, 234}
|
||||
switch a {
|
||||
case 10:
|
||||
println(s0.a)
|
||||
case 11:
|
||||
println(s0.b)
|
||||
case 13:
|
||||
println(s0.d)
|
||||
default:
|
||||
println(s0.c[a])
|
||||
}
|
||||
}
|
||||
|
||||
func test_struct1(a int, b int, c int) {
|
||||
s0 := [2]t0{{11, -12, [4]f64{0.1, -0.2}, 13}, {21, -22, [4]f64{1.1, -1.2}, 24}}
|
||||
switch a {
|
||||
case 10:
|
||||
println(s0[b].a)
|
||||
case 11:
|
||||
println(s0[b].b)
|
||||
case 13:
|
||||
println(s0[b].d)
|
||||
default:
|
||||
println(s0[b].c[c])
|
||||
}
|
||||
}
|
||||
|
||||
type struct_t0 struct {
|
||||
arr0 [16]int
|
||||
arr1 [16]f32
|
||||
}
|
||||
var st0 struct_t0
|
||||
var st1 struct_t0
|
||||
|
||||
func test_struct2() {
|
||||
for i := 0; i < 16; i++ {
|
||||
st0.arr0[i] = i * i
|
||||
st0.arr1[i] = f32(i) * 2.0
|
||||
}
|
||||
}
|
||||
|
||||
func getst0int(st0 struct_t0, i int) int {
|
||||
return st0.arr0[i]
|
||||
}
|
||||
|
||||
func getst0f32(st0 *struct_t0, i int) f32 {
|
||||
return st0.arr1[i]
|
||||
}
|
||||
|
||||
func test_struct3() {
|
||||
for i := 6; i < 14; i += 2 {
|
||||
println(getst0int(st0, i), ", ", getst0f32(&st0, i + 1))
|
||||
}
|
||||
}
|
||||
|
||||
func maket0a(i int) struct_t0 {
|
||||
var r struct_t0
|
||||
for j := 0; j < 16; j++ {
|
||||
r.arr0[j] = 100 - j - i
|
||||
r.arr1[j] = 200.0 - f32(j + i)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func maket0b(i int) *struct_t0 {
|
||||
for j := 0; j < 16; j++ {
|
||||
st1.arr0[j] = 100 - j - i
|
||||
st1.arr1[j] = 200.0 - f32(j + i)
|
||||
}
|
||||
return &st1
|
||||
}
|
||||
|
||||
func test_struct4() {
|
||||
r0, r1 := maket0a(20), maket0b(30)
|
||||
for i := 0; i < 16; i++ {
|
||||
println(r0.arr0[i], ", ", r0.arr1[i], " : ", r1.arr0[i], ", ", r1.arr1[i])
|
||||
}
|
||||
}
|
||||
|
||||
type fff32 f32
|
||||
var arr0 [32]fff32
|
||||
var arr1 [32]fff32
|
||||
|
||||
func test_array2() {
|
||||
for i := 0; i < 32; i++ {
|
||||
arr0[i] = fff32(i * i)
|
||||
}
|
||||
}
|
||||
|
||||
func getarr0a(arr [32]fff32, i int) fff32 {
|
||||
return arr[i]
|
||||
}
|
||||
|
||||
func getarr0b(arr *[32]fff32, i int) fff32 {
|
||||
return arr[i]
|
||||
}
|
||||
|
||||
func makearr0a(i int) [32]fff32 {
|
||||
var r [32]fff32
|
||||
for j := 0; j < 32; j++ {
|
||||
r[j] = fff32(i * j)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func makearr0b(i int) *[32]fff32 {
|
||||
for j := 0; j < 32; j++ {
|
||||
arr1[j] = 2000 - fff32(i * j)
|
||||
}
|
||||
return &arr1
|
||||
}
|
||||
|
||||
func test_array3() {
|
||||
for i := 6; i < 20; i += 2 {
|
||||
println(getarr0a(arr0, i), ", ", getarr0b(&arr0, i + 1))
|
||||
}
|
||||
}
|
||||
|
||||
func test_array4() {
|
||||
r0, r1 := makearr0a(10), makearr0b(20)
|
||||
for i := 6; i < 20; i += 2 {
|
||||
println(r0[i], ", ", r1[i])
|
||||
}
|
||||
}
|
||||
|
||||
func convert_i32_to_i16(a i32) i16 {
|
||||
return i16(a)
|
||||
}
|
||||
|
||||
func convert_i32_to_u16(a i32) u16 {
|
||||
return u16(a)
|
||||
}
|
||||
|
||||
func convert_i16_to_i32(a i16) i32 {
|
||||
return i32(a)
|
||||
}
|
||||
|
||||
func convert_u16_to_i32(a u16) i32 {
|
||||
return i32(a)
|
||||
}
|
||||
|
||||
func test_convert0() {
|
||||
println(i32(convert_i32_to_i16(65537)))
|
||||
println(u32(convert_i32_to_u16(65537)))
|
||||
println(convert_i16_to_i32(100))
|
||||
println(convert_i16_to_i32(-100))
|
||||
println(convert_u16_to_i32(100))
|
||||
println(convert_u16_to_i32(65530))
|
||||
}
|
||||
|
||||
func convert_f64_to_f32(a f64) f32 {
|
||||
return f32(a)
|
||||
}
|
||||
|
||||
func test_convert1() {
|
||||
println(f64(convert_f64_to_f32(3.1415926535)))
|
||||
}
|
||||
|
||||
func convert_i16_to_f64(a i16) f64 {
|
||||
return f64(a)
|
||||
}
|
||||
|
||||
func convert_u16_to_f64(a u16) f64 {
|
||||
return f64(a)
|
||||
}
|
||||
|
||||
func test_convert2() {
|
||||
println(convert_i16_to_f64(100))
|
||||
println(convert_i16_to_f64(-100))
|
||||
println(convert_u16_to_f64(100))
|
||||
println(convert_u16_to_f64(65530))
|
||||
}
|
||||
|
||||
func convert_f64_to_i32(a f64) i32 {
|
||||
return i32(a)
|
||||
}
|
||||
|
||||
func convert_f64_to_u32(a f64) u32 {
|
||||
return u32(a)
|
||||
}
|
||||
|
||||
func test_convert3() {
|
||||
println(convert_f64_to_i32(100.001))
|
||||
println(convert_f64_to_u32(100.001))
|
||||
println(convert_f64_to_i32(99.99))
|
||||
println(convert_f64_to_u32(99.99))
|
||||
println(convert_f64_to_i32(-100.001))
|
||||
println(convert_f64_to_u32(-100.001))
|
||||
}
|
||||
|
||||
func test_print(a i16, b u16, c i8, d u8, e f32) {
|
||||
println(a, ", ", b, ", ", c, ", ", d, ", ", e/2)
|
||||
}
|
||||
|
||||
const gbegin = 10
|
||||
const gend = 20
|
||||
|
||||
func test_global_consts() {
|
||||
for i := gbegin; i < gend; i++ {
|
||||
println(i, " - ", gbegin, " = ", i - gbegin)
|
||||
}
|
||||
}
|
||||
|
||||
type ty0 struct {
|
||||
v0 int
|
||||
v1 float64
|
||||
}
|
||||
var gv0 ty0 = ty0{5555, 3.1415926}
|
||||
var gv1 [4]int = [4]int{81, 82, 17, 76}
|
||||
|
||||
func test_global_variables_0() {
|
||||
println("gv0: {", gv0.v0, ", ", gv0.v1, "}")
|
||||
println("gv1: {", gv1[0], ", ", gv1[1], ", ", gv1[2], ", ", gv1[3], "}")
|
||||
}
|
||||
|
||||
func test_global_variables_1() {
|
||||
gv0.v0 = 8888
|
||||
gv0.v1 = 2.71828
|
||||
gv1[0] += 2
|
||||
gv1[1] += 3
|
||||
gv1[2] += 4
|
||||
gv1[3] += 5
|
||||
println("gv0: {", gv0.v0, ", ", gv0.v1, "}")
|
||||
println("gv1: {", gv1[0], ", ", gv1[1], ", ", gv1[2], ", ", gv1[3], "}")
|
||||
}
|
||||
|
||||
var gint0 int = 100
|
||||
var gint1 int = 200
|
||||
|
||||
func get_int_addr(a int) *int {
|
||||
if a > 0 {
|
||||
return &gint0
|
||||
} else {
|
||||
return &gint1
|
||||
}
|
||||
}
|
||||
|
||||
func set_int(a *int) {
|
||||
*a += 10
|
||||
}
|
||||
|
||||
func test_pointer(a int) {
|
||||
println(gint0, ", ", gint1)
|
||||
p := get_int_addr(a)
|
||||
set_int(p)
|
||||
println(gint0, ", ", gint1)
|
||||
}
|
||||
|
||||
func getPi() f32 {
|
||||
return 3.1415926535
|
||||
}
|
||||
|
||||
func getE() f32 {
|
||||
return 2.71828
|
||||
}
|
||||
|
||||
func minusf32(a f32) f32 {
|
||||
return 0 - a
|
||||
}
|
||||
|
||||
func circle_area(r f32) f32 {
|
||||
return r * r * 3.1415926
|
||||
}
|
||||
|
||||
func test_f32() {
|
||||
println(getPi())
|
||||
println(getE())
|
||||
println(minusf32(getPi()))
|
||||
println(minusf32(-1.3))
|
||||
println(circle_area(1.1))
|
||||
println(circle_area(getE()))
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// 版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
||||
func Println(x int) {
|
||||
println(x)
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/hello",
|
||||
"pkgpath": "pkg",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "prime",
|
||||
"pkgpath": "examples/prime",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/reftoptr",
|
||||
"pkgpath": "reftoptr",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
build:
|
||||
-@rm ./web/*.wasm
|
||||
#go run ../../main.go fmt ./src/main.wa
|
||||
#go run ../../main.go fmt ./src/canvas/canvas.wa
|
||||
|
||||
go run ../../main.go build -o ./web/snake.wasm .
|
||||
|
||||
run:
|
||||
make build
|
||||
cd web && python3 -m http.server
|
||||
|
||||
publish:
|
||||
make build
|
||||
-rm -rf ../../docs/snake/
|
||||
mkdir -p ../../docs/snake
|
||||
cp ./web/* ../../docs/snake/
|
||||
|
||||
clean:
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
wa build _examples/snake
|
||||
wat2wasm a.out.wat
|
||||
copy a.out.wasm _examples/snake/web/snake.wasm
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/snake",
|
||||
"pkgpath": "snake",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/wz/烧脑虚拟机",
|
||||
"pkgpath": "烧脑虚拟机",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/wz/hello",
|
||||
"pkgpath": "hello",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"name": "_examples/wz/素数",
|
||||
"pkgpath": "素数",
|
||||
"version": "0.0.1",
|
||||
"authors": ["author", "author2"],
|
||||
"description": "module description",
|
||||
"readme": "README.md",
|
||||
"homepage": "",
|
||||
"repository": "",
|
||||
"license": "",
|
||||
"license_file": "LICENSE",
|
||||
"keywords": [],
|
||||
"categories": []
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"wa-lang.org/wa/internal/format"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
"wa-lang.org/wa/internal/logger"
|
||||
"wa-lang.org/wa/internal/wamime"
|
||||
)
|
||||
|
||||
// 调试参数
|
||||
|
|
@ -96,9 +97,14 @@ func BuildVFS(cfg *config.Config, vfs *config.PkgVFS, appPkg string) (wat []byte
|
|||
|
||||
// 格式化代码
|
||||
func FormatCode(filename, code string) (string, error) {
|
||||
data, err := format.File(nil, filename, code)
|
||||
data, _, err := format.File(nil, filename, code)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// 获取代码语法类型
|
||||
func GetCodeSyntax(filename string, code []byte) string {
|
||||
return wamime.GetCodeMime(filename, code)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
if err = os.WriteFile(outfile, []byte(wat), 0666); err != nil {
|
||||
return nil, err
|
||||
watBytes, err := BuildFile(cfg, filename, code)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 执行 wat 文件
|
||||
stdoutStderr, err = apputil.RunWasm(cfg, outfile, arg...)
|
||||
// wat 编译为 wasm
|
||||
wasmBytes, err := wabt.Wat2Wasm(watBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
@ -56,6 +56,31 @@ func ExampleRunCode_args() {
|
|||
fmt.Print(string(output))
|
||||
|
||||
// Output:
|
||||
// 0:aa
|
||||
// 1:bb
|
||||
// 0 : aa
|
||||
// 1 : bb
|
||||
}
|
||||
|
||||
func ExampleRunCode_wz() {
|
||||
const code = `
|
||||
#wa:syntax=wz
|
||||
|
||||
引于 "书"
|
||||
|
||||
【启】:
|
||||
书·说:"你好,凹语言中文版!"
|
||||
。
|
||||
`
|
||||
|
||||
output, err := api.RunCode(api.DefaultConfig(), "hello.wa", code)
|
||||
if err != nil {
|
||||
if len(output) != 0 {
|
||||
log.Println(string(output))
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Print(string(output))
|
||||
|
||||
// Output:
|
||||
// 你好,凹语言中文版!
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func main() {
|
|||
}
|
||||
|
||||
const code = `
|
||||
func main() {
|
||||
func main {
|
||||
println(add(40, 2))
|
||||
}
|
||||
|
||||
|
|
@ -40,11 +40,10 @@ func add(a: i32, b: i32) => i32 {
|
|||
}
|
||||
`
|
||||
|
||||
const code_zh = `// 版权 @2022 _examples/hello 作者。保留所有权利。
|
||||
|
||||
const code_zh = `
|
||||
引于 "书"
|
||||
|
||||
【启】:
|
||||
书·说:"你好,凹语言!"
|
||||
书·说:"你好,凹语言中文版!"
|
||||
。
|
||||
`
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<animate attributeName="r" values="0;5;5;0" dur="1s" repeatCount="1" />
|
||||
</circle>
|
||||
|
||||
<path d="M200 230L234 264L268 230M200 230L166 264L132 230" stroke="white" stroke-width="8" stroke-linecap="round" stroke-dasharray="0,0,0,192">
|
||||
<path d="M200 230L234 264L268 230M200 230L166 264L132 230" fill="none" stroke="white" stroke-width="8" stroke-linecap="round" stroke-dasharray="0,0,0,192">
|
||||
<animate attributeType="XML" attributeName="stroke-dasharray" values="0,0,0,192;0,96,96,0; 192,0,0,0" keyTimes="0; 0.5;1" fill="freeze" dur="1.5s" repeatCount="1" />
|
||||
</path>
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
|
|
@ -15,7 +15,7 @@
|
|||
<animate attributeName="r" values="0;5;5;0" dur="1s" repeatCount="1" />
|
||||
</circle>
|
||||
|
||||
<path d="M200 230L234 264L268 230M200 230L166 264L132 230" stroke="white" stroke-width="8" stroke-linecap="round" stroke-dasharray="0,0,0,192">
|
||||
<path d="M200 230L234 264L268 230M200 230L166 264L132 230" fill="none" stroke="white" stroke-width="8" stroke-linecap="round" stroke-dasharray="0,0,0,192">
|
||||
<animate attributeType="XML" attributeName="stroke-dasharray" values="0,0,0,192;0,96,96,0; 192,0,0,0" keyTimes="0; 0.5;1" fill="freeze" dur="1.5s" repeatCount="1" />
|
||||
</path>
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
|
@ -1,4 +1,5 @@
|
|||
<!--
|
||||
|
||||
<!--
|
||||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
// https://wa-lang.org
|
||||
-->
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B |
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
我们计划在完成凹中文前端的重构(即完全脱离现有的Go前端)之时,得到一套稳定的中文语法。
|
||||
|
||||
现在可运行的示例,请参考凹语言工程中的[可执行示例](https://gitee.com/wa-lang/wa/tree/frontend_wz/_examples/wz/)。
|
||||
现在可运行的示例,请参考凹语言工程中的[可执行示例](https://gitee.com/wa-lang/wa/tree/frontend_wz/waroot/examples/wz/)。
|
||||
|
||||
## 简单示例
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ func Point.Length() => int {
|
|||
return math.sqrt(this.x*this.x + this.y*this.y)
|
||||
}
|
||||
|
||||
fn main {
|
||||
func main {
|
||||
p := Point{x:1, y:2}
|
||||
println(p.Length())
|
||||
}
|
||||
|
|
@ -777,4 +777,4 @@ type duck interface {
|
|||
|
||||
届时就可以考虑给中文语法扩充更贴近中文用户使用习惯的特殊新语法了。
|
||||
|
||||
凹中文版的目标,不是让人人都用中文版语法、抛弃英文版语法,而是想抛砖引玉,启发所有人都来设计与开发更适合总国人的编程语言。
|
||||
凹中文版的目标,不是让人人都用中文版语法、抛弃英文版语法,而是想抛砖引玉,启发所有人都来设计与开发更适合中国人的编程语言。
|
||||
|
|
@ -0,0 +1,605 @@
|
|||
# 语言规范
|
||||
|
||||
## 1 EBNF 记法
|
||||
|
||||
凹语言中文语法使用扩展的巴克斯-诺尔范式(EBNF)定义:
|
||||
|
||||
```
|
||||
Production = production_name "=" [ Expression ] "." .
|
||||
Expression = Alternative { "|" Alternative } .
|
||||
Alternative = Term { Term } .
|
||||
Term = production_name | token [ "…" token ] | Group | Option | Repetition .
|
||||
Group = "(" Expression ")" .
|
||||
Option = "[" Expression "]" .
|
||||
Repetition = "{" Expression "}" .
|
||||
```
|
||||
|
||||
其中生成式由表达式构造,表达式通过术和以下操作符构造,自上而下优先级递增(低到高):
|
||||
|
||||
```
|
||||
| 选择
|
||||
() 分组
|
||||
[] 可选(0 或 1 次)
|
||||
{} 重复(0 到 n 次)
|
||||
```
|
||||
|
||||
用小写生成式名用于标识词法标记。使用非最终(Non-terminals)词法标记使用驼峰记法(CamelCase)。而位于双引号 "" 内的即为词法标记。形式 a … b 表示把从 a 到 b 的字符集作为选择项。横向省略号 … 也在本文档中非正式地表示各种列举或简略的代码片断。单个字符 …(不同于三个字符 ...)并非凹语言本身的标记。
|
||||
|
||||
## 2 源文件表示
|
||||
|
||||
为了更好支持中文和其他语言地区的编程,凹语言源码为采用 UTF-8 编码的 Unicode 文本表示。每个码点都是不同的,例如,大写与小写的字母就是不同的字符。同时为了兼容 C 语言字符串的习惯,编译器会阻止字符 NUL(U+0000)出现在源码文本中。此外,如果源文件开头有 BOM 头部,则编译器应该忽略它。为了遵循流行编程语言的习惯,下划线字符被作为字母对待。
|
||||
|
||||
## 3 词法元素
|
||||
|
||||
### 3.1 注释
|
||||
|
||||
凹语言默认采用行注释,以 `//` 开头的表示行注释。注释不能从字符或字符串字面中开始,也不能在其它注释中。行注释可以视做一个换行符。
|
||||
|
||||
### 3.2 词符类型(Token)
|
||||
|
||||
构成凹语言的词符类型有:
|
||||
|
||||
- 关键字,如`若`。
|
||||
- 标识符,如变量名称。
|
||||
- 面值常量,如整数`123`、字符串`"abc"`、
|
||||
- 运算操作符,如`+`、`-`。
|
||||
- 分隔符,如`【】`、`:`。空白字符` `可用于分隔关键字和标识符。
|
||||
|
||||
凹语言程序源码首先会通过词法解析,转化为有效的词符序列,便于后续的语法解析工作。
|
||||
|
||||
### 3.2 基本字符
|
||||
|
||||
凹语言中文版的基本字符包括:
|
||||
|
||||
```
|
||||
数字 = "0" … "9" .
|
||||
16进制数字 = "0" … "9" | "a" … "f" | "A" … "F" .
|
||||
字母 = "a" … "z" | "A" … "Z" | "_" | Unicode字母
|
||||
Unicode字母 = /* 类别为"Letter"的Unicode字符 */ .
|
||||
```
|
||||
|
||||
注:Unicode字符的“Letter”类别是Unicode标准定义的类别。凹语言根据Unicode标准来判定一个字符是否符合“Letter”类别。
|
||||
|
||||
|
||||
### 3.3 标识符(Identifier)
|
||||
|
||||
标识符用做函数、变量、类型等程序实体的名称。一个标识符由一个或多个字母和数字组成。标识符的第一个字符必须是字母,以便于和数字面值区分。
|
||||
|
||||
```
|
||||
标识符 = 字母 { 字母 | 数字 } .
|
||||
```
|
||||
|
||||
标识符的示例:
|
||||
|
||||
```
|
||||
甲
|
||||
a
|
||||
实例
|
||||
_obj
|
||||
z123
|
||||
这是_一个_很长_的_变量
|
||||
```
|
||||
|
||||
### 3.5 关键字
|
||||
|
||||
关键字也是由字母组成的词符,但与标识符不同的是,它们是语言预先定义好的保留词,有特殊的语法意义,不能用于变量名称等。
|
||||
|
||||
凹中文版目前的关键字有:
|
||||
|
||||
```
|
||||
引于 归于 设 之
|
||||
若 则 又若 否则
|
||||
从 到 有 直到
|
||||
自 至 当 为
|
||||
且......或
|
||||
停之 续之
|
||||
```
|
||||
|
||||
凹中文版还处于早期探索阶段,后续可能添加或修改新的关键字。
|
||||
|
||||
### 3.6 运算符
|
||||
|
||||
以下是凹中文版的运算符和与标点:
|
||||
|
||||
```
|
||||
+ - * /
|
||||
+= -= *= /=
|
||||
++ -- ! &
|
||||
【 】 ( )
|
||||
《 》 〈 〉
|
||||
[ ] { }
|
||||
: 。 =>
|
||||
、 ,
|
||||
```
|
||||
|
||||
运算符用于组成表达式,标点用于组成或分隔语句。
|
||||
|
||||
### 3.7 字面值
|
||||
|
||||
字面值是在凹语言代码中明确写出的值,有布尔值、整数、浮点数、字符串、字符几种类型。其中布尔值有`真`、`假`;整数字面值有二进制、八进制、十进制、十六进制几种表示格式;浮点数字面值有小数、科学计数法和十六进制浮点数几种形式;字符串有普通字符串和支持多行的原生字符串几种形式;字符则是有单引号包含表示单个字符。所有的字面值都是无类型的常量。
|
||||
|
||||
普通字符串和字符面值支持的转义符号:
|
||||
|
||||
```
|
||||
\a U+0007 警报或铃声
|
||||
\b U+0008 退格
|
||||
\f U+000C 换页
|
||||
\n U+000A 换行
|
||||
\r U+000D 回车
|
||||
\t U+0009 横向制表符
|
||||
\v U+000b 纵向制表符
|
||||
\\ U+005c 反斜杠
|
||||
\' U+0027 单引号(仅在符文字面中有效)
|
||||
\" U+0022 双引号(仅在字符串字面中有效)
|
||||
```
|
||||
|
||||
以下是常见的字面值常量:
|
||||
|
||||
```
|
||||
# 布尔字面值
|
||||
真
|
||||
假
|
||||
|
||||
# 整数
|
||||
42
|
||||
4_2
|
||||
0600
|
||||
0_600
|
||||
-42
|
||||
|
||||
# 浮点数
|
||||
0.
|
||||
72.40
|
||||
3.1415926
|
||||
1_5. # == 15.0
|
||||
0.15e+0_2 # == 15.0
|
||||
0x1p-2 # == 0.25
|
||||
0x2.p10 # == 2048.0
|
||||
|
||||
# 字符串
|
||||
"凹语言" # UTF-8 输入的文本
|
||||
`凹语言` # UTF-8 输入的原始字面文本
|
||||
"\u65e5\u672c\u8a9e" # 显式的 Unicode 码点
|
||||
"\U000065e5\U0000672c\U00008a9e" # 显式的 Unicode 码点
|
||||
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" # 显式的 UTF-8 字节
|
||||
|
||||
# 字符
|
||||
'凹'
|
||||
'a'
|
||||
'ä'
|
||||
'\t'
|
||||
'\000'
|
||||
'\007'
|
||||
'\xff'
|
||||
'\u12e4'
|
||||
'\U00101234'
|
||||
```
|
||||
|
||||
如果源码将两个码点表示为一个字符,例如包含着重号和字母的结合形式,凹语言会当作两个字面处理(因此不能作为字符面值)。
|
||||
|
||||
## 4 常量
|
||||
|
||||
常量由字面值或常量表达式产生,包含布尔常量、字符常量、整数常量、浮点数常量和字符串常量几种。其中字符、整数、浮点数常量统称为数值常量。
|
||||
|
||||
数值类型常量可表示任意精度的精确值而不受到逻辑现在,因此也就没有整数溢出和 IEEE-754 浮点数的一些限制。常量可以是有类型化的或无类型化的。字面常量、真、假某些只包含无类型化操作数的常量表达式产生的新常量结构也是无类型化的。
|
||||
|
||||
无类型化的常量有一个默认类型,也就是当上下文中需要类型化常量时通过隐式转换出来的类型, 例如在 `设i=0` 这种无显示类型的短变量声明中会被作为`数`(int)类型。
|
||||
|
||||
语言提提供了几个默认类型:
|
||||
|
||||
- `布尔`,即`真`或`假`,对应英文版的`bool`
|
||||
- `字`,即单个字符,如`a`、`甲`等,对应英文版的`rune`
|
||||
- `数`,整数,对应英文版的`int`,整数类型还对应了几个不同位宽的类型,如`数8`表示用8为字节存储的整数。
|
||||
- `小数`,浮点数,对应英文版的`float`
|
||||
- `文`,文本,即字符串,对应英文版的`string`
|
||||
|
||||
尽管数值型常量值域无逻辑限制,但是实现时要求编译器至少有 256bit 表示整数常量和浮点数常量,如果发生溢出则需要在编译阶段给出一个错误。
|
||||
|
||||
## 5 变量
|
||||
|
||||
凹语言中变量是内存用用于保存值的存储位置,或者叫可被取地址的值。变量允许值的集合由其类型确定,比如 `数8` 类型表示 -128 到 127 的整数集合、`布尔`表示 `真`或 `假`的布尔值集合。
|
||||
|
||||
变量又分为具名和匿名两种:具名变量通过标识符引用、而匿名变量通过指针来引用。变量的静态类型在声明时确定,可以明确指定或者由初始化的值类型推导。变量的值可通过在表达式中引用变量取得。如果变量尚未赋值,其值即为其类型的零值。
|
||||
|
||||
零值从直观上表示内存中都是0的状态:布尔类型对应 `假`、数值类型对应 0、字符串对应空字符串`""`、指针和切片对应 `空`。如果是复合类型则其组成的元素均是零值。
|
||||
|
||||
```
|
||||
设甲之布尔 # `甲`是布尔类型
|
||||
设x # x值为空,类型为`万物`(即interface{})
|
||||
设v之*T # v值为空,类型为*T
|
||||
```
|
||||
|
||||
全部的变量组成了程序的状态,也就是所谓的上下文信息。
|
||||
|
||||
## 6 类型
|
||||
|
||||
类型不仅仅决定了值的集合,同时还定义了与该类型值特定的操作集合。已命名类型可通过类型名指定。未命名类型可通过类型字面指定,它将根据既有的类型组成新的类型。
|
||||
|
||||
类型的 EBNF 语法如下:
|
||||
|
||||
```
|
||||
类型定义 = "《" 类型名称 "》" ( "=" 类型面值 | ":" 类型成员列表 "。" ) .
|
||||
类型名称 = 标识符 .
|
||||
类型面值 = 数组类型 | 结构类型 | 指针类型 | 函数类型 | 接口类型 | 切片类型 | 映射类型 .
|
||||
类型成员列表 = 类型成员 { "," 类型成员 } .
|
||||
类型成员 = 成员名称 [ "之" 类型 ] .
|
||||
成员名称 = 标识符 .
|
||||
```
|
||||
|
||||
类型的定义有两种形式,一种是类型别名,用`=`表示,另一种是结构性类型,用`:`后接成员列表。
|
||||
|
||||
布尔值、数值与字符串类型的实例的命名是预声明的`布尔`、`数`、`文`等。 数组、结构体、指针、函数、接口、切片和映射这些复合类型可由类型字面再进行构造。
|
||||
|
||||
下面是定义类型的例子:
|
||||
|
||||
```
|
||||
// 定义一个新的类型别名,名字叫“字节”,它实际上是“正8”整数类型的别名
|
||||
《字节》 = 正8
|
||||
// 定义一个新类型别名,名称叫“缓冲区”,它实际上是一个“字类型”的定长数组,即英文版的`[1024]byte`
|
||||
《缓冲区》 = [字:1024]
|
||||
// 定义一个新的类型别名,名字叫“字典”,它实际上是一个“文”类型的映射,即英文版的`map[string]string`
|
||||
《字典》 = {文:文}
|
||||
|
||||
|
||||
// 定义一个结构性类型,名字叫“点”,它有“横”与“纵”两个成员,分别代表横坐标和纵坐标
|
||||
《点》:
|
||||
横之数
|
||||
纵之数
|
||||
。
|
||||
```
|
||||
|
||||
其中 `字节`、`缓冲区`和`字典`都是类型的别名(即不产生新类型对象),而`点`则产生新的类型。
|
||||
|
||||
### 5.1 布尔类型
|
||||
|
||||
预声明的布尔类型为 bool。布尔类型表示由预声明常量 true 和 false 所代表的布尔值的集。
|
||||
|
||||
### 5.2 数值类型
|
||||
|
||||
数值类型表示整数值和浮点数值的集合。预声明和架构无关的预声明数值类型:
|
||||
|
||||
```
|
||||
u8 所有无符号 8 位整数集(0 到 255)
|
||||
u16 所有无符号 16 位整数集(0 到 65535)
|
||||
u32 所有无符号 32 位整数集(0 到 4294967295)
|
||||
u64 所有无符号 64 位整数集(0 到 18446744073709551615)
|
||||
|
||||
i8 所有带符号 8 位整数集(-128 到 127)
|
||||
i16 所有带符号 16 位整数集(-32768 到 32767)
|
||||
i32 所有带符号 32 位整数集(-2147483648 到 2147483647)
|
||||
i64 所有带符号 64 位整数集(-9223372036854775808 到 9223372036854775807)
|
||||
|
||||
f32 所有 IEEE-754 32 位浮点数集
|
||||
f64 所有 IEEE-754 64 位浮点数集
|
||||
|
||||
byte u8 的别名
|
||||
rune i32 的别名
|
||||
```
|
||||
|
||||
比如 i32 表示用 32bit 二进制补码表示的整数值集合。
|
||||
|
||||
此外还有大小取决于具体实现的预声明数值类型:
|
||||
|
||||
```
|
||||
uint 32 或 64 位
|
||||
int 大小与 uint 相同
|
||||
uintptr 大到足以存储指针值无解释位的无符号整数
|
||||
```
|
||||
|
||||
但是对应凹语言来说,目前 uint/int/uintptr 在 WebAssembly 平台都是用 32bit 表示。
|
||||
|
||||
### 5.3 字符串类型
|
||||
|
||||
字符串类型为 string,表示字符串值的集,字符串的值为字节序列。字符串值是只读不可修改的(不是绑定到某个字符串值的变量):一旦被创建,字符串的内容就不能更改。
|
||||
|
||||
可使用内建函数 `len` 获取字符串 `s` 的长度。若该字符串为常量,则其长度即为编译时常量。 字符串的字节可通过整数 `0` 至 `len(s)-1` 访问。获取这样一个元素的地址是非法的:比如通过 `&s[i]` 访问字符串的第 `i` 个字节的地址是禁止的。
|
||||
|
||||
### 5.4 数组类型
|
||||
|
||||
数组是单一类型元素的序列,其中长度也是数组类型的一部分。数组的 EBNF 语法如下:
|
||||
|
||||
```
|
||||
ArrayType = "[" ArrayLength "]" ElementType .
|
||||
ArrayLength = Expression .
|
||||
ElementType = Type .
|
||||
```
|
||||
|
||||
可使用内建函数 `len` 获取数组的长度,返回的结果一个常量。可通过整数下标 `0` 到 `len(a)-1` 访问数组的元素。数组类型总是一维的,但可组合构成多维的类型。
|
||||
|
||||
```
|
||||
[32]byte
|
||||
[2*N] struct { x, y :i32 }
|
||||
[9527]*f64
|
||||
[3][5]int
|
||||
[2][2][2]f64 // 等价于[2]([2]([2]f64))
|
||||
```
|
||||
|
||||
数组的长度可以使用常量表达式、或者是由初始化面值推导,数组的元素可以是数组从而可以构成多维数组。
|
||||
|
||||
### 5.5 切片类型
|
||||
|
||||
切片类型是在数组类型基础上抽象的类型,切片类型不包含底层数组的长度信息。切片类型底层是对某个数组的引用,未初始化切片的值为 nil。
|
||||
|
||||
可使用内建函数 `len` 获取切片的长度,返回的结果一个变量。可使用内建函数 `cap` 获取切片的容量,返回的结果一个变量。可通过整数下标 `0` 到 `len(a)-1` 访问切片的元素。切片一旦初始化,就总是引用一个包含其元素的底层数组。 因此,切片与引用同一个数组的其他切片共享存储;与此相反,不同的数组总是表示其不同的存储。
|
||||
|
||||
可以通过 make 内置函数创建切片(容量参赛是可选的):
|
||||
|
||||
```
|
||||
make([]T, length)
|
||||
make([]T, length, capacity)
|
||||
```
|
||||
|
||||
类似于数组,切片总是一维的,但可组合构造多维切片对象。因为多维切片和多维数组的底层数据布局差异,其内部的切片必须单独进行初始化,但是带来的一个灵活性是不同子切片大小可以不同。
|
||||
|
||||
### 5.6 结构体类型
|
||||
|
||||
结构体和数组类似,数组采用下标来组织相同类型的值,而结构体定义命名的不同类型的元素序列。结构体类型中每一个元素都有一个名字和类型。字段名可显示地指定或通过匿名嵌入字段隐式地指定。在结构体中,字段名必须是唯一的。
|
||||
|
||||
```
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList ":" Type | EmbeddedField) [ Tag ] .
|
||||
EmbeddedField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
```
|
||||
|
||||
下面是结构体的例子:
|
||||
|
||||
```
|
||||
# 空结构体
|
||||
struct {}
|
||||
|
||||
# 带 6 个字段的结构体
|
||||
struct {
|
||||
x, y :int
|
||||
u :f32
|
||||
_ :f32 # 填充
|
||||
A :*[]int
|
||||
F :func()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
通过有类型而无显式字段名声明的字段为匿名字段,亦称为嵌入式字段或该结构体中此种类型的嵌入。 这种字段类型必须作为一个类型名 T 或一个非接口类型名的指针 *T来实现, 且 T 本身不能为指针类型。未限定类型名的行为类似于字段名。
|
||||
|
||||
```
|
||||
# 匿名字段的结构体类型
|
||||
struct {
|
||||
T1 # 字段名为 T1
|
||||
*T2 # 字段名为 T2
|
||||
P.T3 # 字段名为 T3
|
||||
*P.T4 # 字段名为 T4
|
||||
x, y :i32 # 字段名为 x 和 y
|
||||
}
|
||||
```
|
||||
|
||||
字段声明可后跟一个可选的字符串字面值标注:
|
||||
|
||||
```
|
||||
struct {
|
||||
x, y : f64 "" # 空标注字符串相当于没有标注
|
||||
name : string "任何字符串都允许作为标注"
|
||||
_ : [4]byte "这不是一个结构体字段"
|
||||
}
|
||||
```
|
||||
|
||||
标注是结构体的类型标识的一部分,未来可通过反射机制获得。
|
||||
|
||||
### 5.7 指针类型
|
||||
|
||||
指针类型表示一个指向所有给定类型变量的指针的集合,其中指向的类型称为指针的基础类型。未初始化的指针的值为 nil。
|
||||
|
||||
```
|
||||
PointerType = "*" BaseType .
|
||||
BaseType = Type .
|
||||
```
|
||||
|
||||
下面是指针类型的例子:
|
||||
|
||||
```
|
||||
*Point
|
||||
*[4]int
|
||||
```
|
||||
|
||||
凹语言指针和传统的 C/C++ 指针并不完全相同,目前的实现更偏向引用。
|
||||
|
||||
### 5.8 函数类型
|
||||
|
||||
凹语言函数类型表示所有带相同形参和返回类型的集合。未初始化的函数类型变量的值为 nil。
|
||||
|
||||
```
|
||||
FunctionType = "func" Signature .
|
||||
Signature = Parameters [ "=>" Result ] .
|
||||
Result = Parameters | ":" Type .
|
||||
Parameters = "(" [ ParameterList [ "," ] ] ")" .
|
||||
ParameterList = ParameterDecl { "," ParameterDecl } .
|
||||
ParameterDecl = [ IdentifierList ] ":" [ "..." ] Type .
|
||||
```
|
||||
|
||||
在形参或结果的列表中,其名称必须都存在或都不存在。 若存在,则每个名称代表一个指定类型的项,所有在签名中的非空白名称必须是唯一的。 若不存在,则每个类型代表一个此类型的项。若恰好有一个未命名的值,它可能写作一个不加括号的类型,除此之外,形参和结果的列表总是在括号中。
|
||||
|
||||
函数签名中加入的最后一个形参可能有一个带 `...` 前缀的类型。 带这样形参的函数被称为变参函数,它可接受零个或多个实参。
|
||||
|
||||
```
|
||||
func()
|
||||
func(x :int) => int
|
||||
func(a, _ :int, z :f32) => bool
|
||||
func(a, b :int, z :f32) => (bool)
|
||||
func(prefix :string, values :...int)
|
||||
func(a, b :int, z :f64, opt :...any) => (success bool)
|
||||
func(int, int, f64) => (f64, *[]int)
|
||||
func(n :int) => func(p :*T)
|
||||
```
|
||||
|
||||
函数的类型可以用于全局函数、闭包函数、方法值等地方。
|
||||
|
||||
### 5.9 接口类型
|
||||
|
||||
接口类型定义满足方法列表的类型结合。目前只有结构体才能定义方法。未初始化的接口类型变量的值为 nil。
|
||||
|
||||
```
|
||||
InterfaceType = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
|
||||
MethodSpec = MethodName Signature .
|
||||
MethodName = identifier .
|
||||
InterfaceTypeName = TypeName .
|
||||
```
|
||||
|
||||
下面是一个接口类型定义的方法集合:
|
||||
|
||||
```
|
||||
# 一个简单的 File 接口
|
||||
interface {
|
||||
Read(b :[]byte) => (n :int, err :error)
|
||||
Write(b :[]byte) => (n :int, err :error)
|
||||
Close()
|
||||
}
|
||||
```
|
||||
|
||||
如果 T 类型有着接口定义的方法,那么就表示实现了接口:
|
||||
|
||||
```
|
||||
func T.Read(p :[]byte) => (n :int, err :error)
|
||||
func T.Write(p :[]byte) => (n :int, err :error)
|
||||
func T.Close() error
|
||||
```
|
||||
|
||||
其中没有定义方法集的 `interface{}` 接口可以包含任何类型的值,同时它有一个 `any` 别名。
|
||||
|
||||
### 5.10 map类型
|
||||
|
||||
map 是一个同种类型元素的无序组,该类型称为元素类型 映射通过另一类型唯一的键集索引,该类型称为键类型。未初始化的映射值为 nil。
|
||||
|
||||
```
|
||||
MapType = "map" "[" KeyType "]" ElementType .
|
||||
KeyType = Type .
|
||||
```
|
||||
|
||||
比较操作符 `==`和 `!=` 必须由键类型的操作数完全定义;因此键类型不能是函数、映射或切片。若该键类型为接口类型,这些比较运算符必须由动态键值定义;失败将导致一个异常.
|
||||
|
||||
同样通过 make 内置函数创建一个 map:
|
||||
|
||||
```
|
||||
make(map[string]int)
|
||||
make(map[string]int, 100)
|
||||
```
|
||||
|
||||
make创建map时,第二个是一个可选的容量估计参数。
|
||||
|
||||
## 6 类型与值的性质
|
||||
|
||||
## 6.1 类型标识
|
||||
|
||||
若两个已命名类型的类型名源自相同的类型实现,它们就是相同的。一个已命名类型和一个未命名类型总不相同。若两个未命名类型其相应的类型字面相同,那么它们的类型相同,即它们的字面结构是否相同且其相应的组件类型是否相同。细节详述:
|
||||
|
||||
- 若两个数组类型其元素类型相同且长度相同,那么它们的类型相同。
|
||||
- 若两个切片类型其元素类型相同,那么它们的类型相同。
|
||||
- 若两个结构体类型其字段序列相同、相应字段名相同、类型相同、标注相同,那么它们的类型相同。两个匿名字段根据其名字被判定是否相同。
|
||||
- 若两个指针类型其基础类型相同,那么它们的类型相同。
|
||||
- 若两个函数类型其形参个数相同、返回值相同、相应形参类型相同、返回值类型相同,那么它们的类型相同。形参和返回值名无需匹配。
|
||||
- 若两个接口类型其方法集相同、名字相同、函数类型相同,那么它们的类型相同。出自不同包的小写方法名总不相同。两接口类型是否相同与方法的次序无关。
|
||||
- 若两个map类型其键值类型相同,那么它们的类型相同。
|
||||
|
||||
若两个类型非相同即为不同。
|
||||
|
||||
|
||||
## 6.2 可赋值性
|
||||
|
||||
在下列情况下,值 x 可赋予类型为 T 的变量:
|
||||
|
||||
- 当 x 的类型和 T 相同时。
|
||||
- 当 x 的类型 V 和 T 有相同的 底层类型 且在 V 或 T 中至少有一个不是已命名类型时。
|
||||
- 当 T 为接口类型且 x 实现了 T 时。
|
||||
- 当 x 为预声明标识符 nil 且 T 为指针、函数、切片、映射、通道或接口类型时。
|
||||
- 当 x 为无类型化,可通过类型 T 的值来表示的 常量时。
|
||||
|
||||
任何类型都可赋予下划线表示的空白标识符.
|
||||
|
||||
## 6.3 可表示性
|
||||
|
||||
若满足以下条件,则常量 x 可表示为 T 类型的值:
|
||||
|
||||
- x 在由 T 所确定的值的集合中。
|
||||
- T 为浮点类型且 x 可被舍入到 T 的精度而不会溢出。
|
||||
|
||||
下面是一个例子:
|
||||
|
||||
```
|
||||
x T x 可表示为一个 T 类型的值,因为
|
||||
|
||||
'a' byte 97 属于 byte 值的集合
|
||||
97 rune rune 是 int32 的别名,且 97 属于 32 位整数值的集合
|
||||
"foo" string "foo" 属于 string 值的集合
|
||||
1024 int16 1024 属于 16 位整数的集合
|
||||
42.0 byte 42 属于无符号 8 位整数的集合
|
||||
1e10 u64 10000000000 属于 64 位无符号整数的集合
|
||||
2.718281828459045 f32 2.718281828459045 舍入到 2.7182817,它属于 f32 值的集合
|
||||
-1e-1000 f64 -1e-1000 舍入到 IEEE -0.0,它进一步简化为 0.0
|
||||
0i int 0 是一个整数值
|
||||
|
||||
x T x 无法表示为一个 T 类型的值,因为
|
||||
|
||||
0 bool 0 不属于布尔值的集合
|
||||
'a' string 'a' 是一个 rune,它不属于 string 值的集合
|
||||
1024 byte 1024 不属于无符号 8 位整数的集合
|
||||
-1 uint16 -1 不属于 16 位无符号整数的集合
|
||||
1.1 int 1.1 不是一个整数值
|
||||
1e1000 f64 1e1000 舍入后溢出到 IEEE +Inf
|
||||
```
|
||||
|
||||
## 7 代码块
|
||||
|
||||
凹语言代码块为一对大括号括住的,可能为空的一系列声明和语句。
|
||||
|
||||
```
|
||||
Block = "{" StatementList "}" .
|
||||
StatementList = { Statement ";" } .
|
||||
```
|
||||
|
||||
除显式源码块外,还有一些隐式块:
|
||||
|
||||
- 全域块包含所有的 凹语言 源码文本。
|
||||
- 每个包都有包含其所有 凹语言 源码文本的包块。
|
||||
- 每个文件都有包含其所有 凹语言 源码文本的文件块。
|
||||
- 每个 if、for 和 switch 语句都被视为处于其自身的隐式块中。
|
||||
- 每个 switch 语句中的子句其行为如同隐式块。
|
||||
|
||||
块可嵌套并会影响作用域。
|
||||
|
||||
## 8 声明与作用域
|
||||
|
||||
凹语言中声明可将非空白标识符绑定到一个常量、类型、变量、函数、标签或包。在程序中,每个标识符都必须被声明才能被使用。同一标识符不能在同一代码块中声明两次,且在文件与包代码块中不能同时声明。
|
||||
|
||||
下划线表示的空白标识符可像其它标识符一样在声明中使用,但它不会引入绑定被视作未声明的。在包块中,标识符 init 仅能用作 init 函数声明,且与空白标识符一样不会引入新的绑定。
|
||||
|
||||
```
|
||||
Declaration = ConstDecl | TypeDecl | VarDecl .
|
||||
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
|
||||
```
|
||||
|
||||
已声明标识符的作用域即为该标识符所表示的具名常量、类型、变量、函数、标签或包在源文本中的作用范围。
|
||||
|
||||
凹语言 使用块表示词法作用域:
|
||||
|
||||
1. 预声明标识符的作用域为全域块。
|
||||
1. 即在任何函数之外的顶级声明的表示常量、类型、变量或函数的标识符其作用域为该包块。
|
||||
1. 已导入包的包名作用域为仅包含该导入声明的文件块。
|
||||
1. 表示方法接收器 this、函数形参或返回值变量的标识符,其作用域为该函数体。
|
||||
1. 在函数中声明为常量或变量的标识符,其作用域始于该函数中具体常量实现或变量实现的结尾,止于最内部包含块的结尾。
|
||||
1. 在函数中声明为类型的标识符,其作用域始于该函数中具体类型实现的标识符,止于最内部包含块的结尾。
|
||||
|
||||
在块中短声明的标识符可在其内部块中重新声明。
|
||||
|
||||
## 8.1 标签作用域
|
||||
|
||||
## 8.2 空白标识符
|
||||
|
||||
|
||||
## 9 表达式
|
||||
|
||||
## 10 语句
|
||||
|
||||
## 11 包结构
|
||||
|
||||
## 12 初始化和执行顺序
|
||||
|
||||
|
||||
|
||||
3
go.mod
|
|
@ -5,6 +5,7 @@ module wa-lang.org/wa
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/tetratelabs/wazero v1.0.0-pre.4
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
wa-lang.org/wabt-go v1.3.0
|
||||
wa-lang.org/wazero v1.0.1
|
||||
)
|
||||
|
|
|
|||
4
go.sum
|
|
@ -1,6 +1,10 @@
|
|||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/tetratelabs/wazero v1.0.0-pre.4 h1:RBJQT5OzmORkSp6MmZDWoFEr0zXjk4pmvMKAdeUnsaI=
|
||||
github.com/tetratelabs/wazero v1.0.0-pre.4/go.mod h1:u8wrFmpdrykiFK0DFPiFm5a4+0RzsdmXYVtijBKqUVo=
|
||||
wa-lang.org/wabt-go v1.2.3 h1:xwGW3+IrCMAPDx+ngQFx7vl0xBc1vuooRNql7i2yCiA=
|
||||
wa-lang.org/wabt-go v1.2.3/go.mod h1:v4fKY/p4oMiKwQ2S2aC/wOXvJ2bk2MgtLmwK0TKTNx0=
|
||||
wa-lang.org/wabt-go v1.3.0 h1:7oSrr80hGlf9G6wwf2pQQ1NXbeqBxEk3D3GDWwbYmVA=
|
||||
wa-lang.org/wabt-go v1.3.0/go.mod h1:v4fKY/p4oMiKwQ2S2aC/wOXvJ2bk2MgtLmwK0TKTNx0=
|
||||
wa-lang.org/wazero v1.0.1 h1:6o6M2o6TP0c6b1+jJtfFaAJQGBIk8HrDegIGzrtwmGc=
|
||||
wa-lang.org/wazero v1.0.1/go.mod h1:r1lGhhlgAnh5eZyNHe/dslT0LF7aY3qZGJWCbxRqf3E=
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"wa-lang.org/wa/internal/config"
|
||||
"wa-lang.org/wa/internal/logger"
|
||||
)
|
||||
|
||||
// 命令行程序对象
|
||||
type App struct {
|
||||
opt Option
|
||||
path string
|
||||
src string
|
||||
}
|
||||
|
||||
// 构建命令行程序对象
|
||||
func NewApp(opt *Option) *App {
|
||||
logger.Tracef(&config.EnableTrace_app, "opt: %+v", opt)
|
||||
|
||||
p := &App{}
|
||||
if opt != nil {
|
||||
p.opt = *opt
|
||||
}
|
||||
if p.opt.Clang == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
p.opt.Clang, _ = exec.LookPath("clang.exe")
|
||||
} else {
|
||||
p.opt.Clang, _ = exec.LookPath("clang")
|
||||
}
|
||||
if p.opt.Clang == "" {
|
||||
p.opt.Clang = "clang"
|
||||
}
|
||||
}
|
||||
if p.opt.Llc == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
p.opt.Llc, _ = exec.LookPath("llc.exe")
|
||||
} else {
|
||||
p.opt.Llc, _ = exec.LookPath("llc")
|
||||
}
|
||||
if p.opt.Llc == "" {
|
||||
p.opt.Llc = "llc"
|
||||
}
|
||||
}
|
||||
if p.opt.TargetOS == "" {
|
||||
p.opt.TargetOS = config.WaOS_Default
|
||||
}
|
||||
if p.opt.TargetArch == "" {
|
||||
p.opt.TargetArch = config.WaArch_Default
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *App) GetConfig() *config.Config {
|
||||
return p.opt.Config()
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// 版权 @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)
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// 版权 @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
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/format"
|
||||
)
|
||||
|
||||
func (p *App) Fmt(path string) error {
|
||||
if path == "" {
|
||||
path, _ = os.Getwd()
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, "...") {
|
||||
panic("TODO: fmt dir/...")
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
panic("TODO: fmt dir")
|
||||
}
|
||||
|
||||
code, err := format.File(nil, path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path, code, 0666)
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var s scanner.Scanner
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile(filename, fset.Base(), len(src))
|
||||
s.Init(file, src, nil, scanner.ScanComments)
|
||||
|
||||
for {
|
||||
pos, tok, lit := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
"wa-lang.org/wa/internal/app/apputil"
|
||||
"wa-lang.org/wa/internal/backends/compiler_wat"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
)
|
||||
|
||||
func (p *App) RunTest(pkgpath string, appArgs ...string) error {
|
||||
cfg := p.opt.Config()
|
||||
cfg.UnitTest = true
|
||||
prog, err := loader.LoadProgram(cfg, pkgpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
mainPkg := prog.Pkgs[prog.Manifest.MainPkg]
|
||||
|
||||
if len(mainPkg.TestInfo.Files) == 0 {
|
||||
fmt.Printf("? %s [no test files]\n", prog.Manifest.MainPkg)
|
||||
return nil
|
||||
}
|
||||
|
||||
var firstError error
|
||||
for _, t := range mainPkg.TestInfo.Tests {
|
||||
output, err := compiler_wat.New().Compile(prog, t.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile("a.out.wat", []byte(output), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout, stderr, err := apputil.RunWasmEx(cfg, "a.out.wat", appArgs...)
|
||||
|
||||
stdout = bytes.TrimSpace(stdout)
|
||||
bOutputOK := t.Output == string(stdout)
|
||||
|
||||
if err == nil && bOutputOK {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
if _, ok := err.(*sys.ExitError); ok {
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
if s := sWithPrefix(string(stdout), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
if s := sWithPrefix(string(stderr), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Output != "" {
|
||||
if expect, got := t.Output, string(stdout); expect != got {
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect = %q, got = %q", expect, got)
|
||||
}
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect = %q, got = %q\n", expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, t := range mainPkg.TestInfo.Examples {
|
||||
output, err := compiler_wat.New().Compile(prog, t.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile("a.out.wat", []byte(output), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout, stderr, err := apputil.RunWasmEx(cfg, "a.out.wat", appArgs...)
|
||||
|
||||
stdout = bytes.TrimSpace(stdout)
|
||||
bOutputOK := t.Output == string(stdout)
|
||||
|
||||
if err == nil && bOutputOK {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
if _, ok := err.(*sys.ExitError); ok {
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
if s := sWithPrefix(string(stdout), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
if s := sWithPrefix(string(stderr), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Output != "" {
|
||||
if expect, got := t.Output, string(stdout); expect != got {
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect = %q, got = %q", expect, got)
|
||||
}
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect = %q, got = %q\n", expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
if firstError != nil {
|
||||
fmt.Printf("FAIL %s %v\n", prog.Manifest.MainPkg, time.Now().Sub(startTime).Round(time.Millisecond))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("ok %s %v\n", prog.Manifest.MainPkg, time.Now().Sub(startTime).Round(time.Millisecond))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sWithPrefix(s, prefix string) string {
|
||||
lines := strings.Split(strings.TrimSpace(s), "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = prefix + strings.TrimSpace(line)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
"wa-lang.org/wa/internal/ssa"
|
||||
)
|
||||
|
||||
func (p *App) SSA(filename string) error {
|
||||
cfg := p.opt.Config()
|
||||
prog, err := loader.LoadProgram(cfg, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog.SSAMainPkg.WriteTo(os.Stdout)
|
||||
|
||||
var funcNames []string
|
||||
for name, x := range prog.SSAMainPkg.Members {
|
||||
if _, ok := x.(*ssa.Function); ok {
|
||||
funcNames = append(funcNames, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(funcNames)
|
||||
for _, s := range funcNames {
|
||||
prog.SSAMainPkg.Func(s).WriteTo(os.Stdout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *App) 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
|
||||
}
|
||||
|
||||
func (p *App) isWaFile(path string) bool {
|
||||
if fi, err := os.Lstat(path); err == nil && fi.Mode().IsRegular() {
|
||||
return strings.HasSuffix(strings.ToLower(path), ".wa")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/backends/compiler_wat"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
)
|
||||
|
||||
func (p *App) WASM(filename string) ([]byte, error) {
|
||||
if _, err := os.Lstat(filename); err != nil {
|
||||
return nil, fmt.Errorf("%q not found", filename)
|
||||
}
|
||||
cfg := p.opt.Config()
|
||||
prog, err := loader.LoadProgram(cfg, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 凹中文的源码启动函数为【启】,对应的wat函数名应当是"$0xe590af"
|
||||
main := "main"
|
||||
if strings.HasSuffix(filename, ".wz") {
|
||||
main = "$0xe590af"
|
||||
}
|
||||
|
||||
// 如果是运行整个package,则判断主包里是否有名为【启】的函数,如果有,则将其作为启动函数
|
||||
if filename == "." {
|
||||
for k := range prog.SSAMainPkg.Members {
|
||||
if k == "启" && prog.SSAMainPkg.Members[k].Type().Underlying().String() == "func()" {
|
||||
main = "$0xe590af"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output, err := compiler_wat.New().Compile(prog, main)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(output), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/ast"
|
||||
"wa-lang.org/wa/internal/parser"
|
||||
"wa-lang.org/wa/internal/token"
|
||||
)
|
||||
|
||||
var CmdAst = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "ast",
|
||||
Usage: "parse Wa source code and print ast",
|
||||
Action: CmdAstAction,
|
||||
}
|
||||
|
||||
func CmdAstAction(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := PrintAST(c.Args().First())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PrintAST(filename string) error {
|
||||
if !appbase.HasExt(filename, ".wa", ".wz") {
|
||||
return fmt.Errorf("%q is not Wa file", filename)
|
||||
}
|
||||
|
||||
if !appbase.PathExists(filename) {
|
||||
return fmt.Errorf("%q not found", filename)
|
||||
}
|
||||
if !appbase.IsNativeFile(filename) {
|
||||
return fmt.Errorf("%q must be file", filename)
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(nil, fset, filename, nil, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ast.Print(fset, f)
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package appbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
)
|
||||
|
||||
// 输出路径
|
||||
func MakeFlag_output() *cli.StringFlag {
|
||||
return &cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "set output file",
|
||||
Value: "",
|
||||
}
|
||||
}
|
||||
|
||||
// 构建的目标
|
||||
func MakeFlag_target() *cli.StringFlag {
|
||||
return &cli.StringFlag{
|
||||
Name: "target",
|
||||
Usage: fmt.Sprintf("set target os (%s)", strings.Join(config.WaOS_List, "|")),
|
||||
Value: config.WaOS_Default,
|
||||
}
|
||||
}
|
||||
|
||||
// 构建的 Tags
|
||||
func MakeFlag_tags() *cli.StringFlag {
|
||||
return &cli.StringFlag{
|
||||
Name: "tags",
|
||||
Usage: "set build tags",
|
||||
}
|
||||
}
|
||||
|
||||
func MakeFlag_ld_stack_size() *cli.IntFlag {
|
||||
return &cli.IntFlag{
|
||||
Name: "ld-stack-size",
|
||||
Usage: "set stack size",
|
||||
}
|
||||
}
|
||||
|
||||
func MakeFlag_ld_max_memory() *cli.IntFlag {
|
||||
return &cli.IntFlag{
|
||||
Name: "ld-max-memory",
|
||||
Usage: "set max memory size",
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package appbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
)
|
||||
|
||||
// 命令行选项
|
||||
type Option struct {
|
||||
Debug bool
|
||||
WaBackend string
|
||||
BuilgTags []string
|
||||
TargetArch string
|
||||
TargetOS string
|
||||
Clang string
|
||||
Llc string
|
||||
LD_StackSize int
|
||||
LD_MaxMemory int
|
||||
}
|
||||
|
||||
func (opt *Option) Config() *config.Config {
|
||||
cfg := config.DefaultConfig()
|
||||
|
||||
if opt.Debug {
|
||||
cfg.Debug = true
|
||||
}
|
||||
if opt.WaBackend != "" {
|
||||
cfg.WaBackend = opt.WaBackend
|
||||
}
|
||||
if len(opt.BuilgTags) > 0 {
|
||||
cfg.BuilgTags = append(cfg.BuilgTags, opt.BuilgTags...)
|
||||
}
|
||||
if opt.TargetArch != "" {
|
||||
cfg.WaArch = opt.TargetArch
|
||||
}
|
||||
if opt.TargetOS != "" {
|
||||
cfg.WaOS = opt.TargetOS
|
||||
}
|
||||
if opt.LD_StackSize != 0 {
|
||||
cfg.LDFlags.StackSize = opt.LD_StackSize
|
||||
}
|
||||
if opt.LD_MaxMemory != 0 {
|
||||
cfg.LDFlags.MaxMemory = opt.LD_MaxMemory
|
||||
}
|
||||
|
||||
switch cfg.WaArch {
|
||||
case "wasm":
|
||||
cfg.WaSizes.MaxAlign = 8
|
||||
cfg.WaSizes.WordSize = 4
|
||||
case "wasm64":
|
||||
cfg.WaSizes.MaxAlign = 8
|
||||
cfg.WaSizes.WordSize = 8
|
||||
case "amd64":
|
||||
cfg.WaSizes.MaxAlign = 8
|
||||
cfg.WaSizes.WordSize = 8
|
||||
case "arm64":
|
||||
cfg.WaSizes.MaxAlign = 8
|
||||
cfg.WaSizes.WordSize = 8
|
||||
case "native":
|
||||
cfg.WaSizes.MaxAlign = 8
|
||||
cfg.WaSizes.WordSize = 8
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// 构建命令行程序对象
|
||||
func (opt *Option) Adjust() {
|
||||
if opt.Clang == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
opt.Clang, _ = exec.LookPath("clang.exe")
|
||||
} else {
|
||||
opt.Clang, _ = exec.LookPath("clang")
|
||||
}
|
||||
if opt.Clang == "" {
|
||||
opt.Clang = "clang"
|
||||
}
|
||||
}
|
||||
if opt.Llc == "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
opt.Llc, _ = exec.LookPath("llc.exe")
|
||||
} else {
|
||||
opt.Llc, _ = exec.LookPath("llc")
|
||||
}
|
||||
if opt.Llc == "" {
|
||||
opt.Llc = "llc"
|
||||
}
|
||||
}
|
||||
if opt.TargetOS == "" {
|
||||
opt.TargetOS = config.WaOS_Default
|
||||
}
|
||||
if opt.TargetArch == "" {
|
||||
opt.TargetArch = config.WaArch_Default
|
||||
}
|
||||
}
|
||||
|
||||
func BuildOptions(c *cli.Context, waBackend ...string) *Option {
|
||||
opt := &Option{
|
||||
Debug: c.Bool("debug"),
|
||||
WaBackend: config.WaBackend_Default,
|
||||
BuilgTags: strings.Fields(c.String("tags")),
|
||||
Clang: c.String("clang"),
|
||||
Llc: c.String("llc"),
|
||||
LD_StackSize: c.Int("ld-stack-size"),
|
||||
LD_MaxMemory: c.Int("ld-max-memory"),
|
||||
}
|
||||
|
||||
opt.TargetArch = "wasm"
|
||||
if len(waBackend) > 0 {
|
||||
opt.WaBackend = waBackend[0]
|
||||
}
|
||||
|
||||
if target := c.String("target"); !config.CheckWaOS(target) {
|
||||
fmt.Printf("unknown target: %s\n", c.String("target"))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch c.String("target") {
|
||||
case "", "wa", "walang":
|
||||
opt.TargetOS = config.WaOS_Default
|
||||
case config.WaOS_wasi:
|
||||
opt.TargetOS = config.WaOS_wasi
|
||||
case config.WaOS_arduino:
|
||||
opt.TargetOS = config.WaOS_arduino
|
||||
case config.WaOS_chrome:
|
||||
opt.TargetOS = config.WaOS_chrome
|
||||
case config.WaOS_mvp:
|
||||
opt.TargetOS = config.WaOS_mvp
|
||||
default:
|
||||
fmt.Printf("unreachable: target: %s\n", c.String("target"))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
opt.Adjust()
|
||||
return opt
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package appbase
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// 路径名是否包含后缀, 忽略大小写区别
|
||||
func HasExt(path string, exts ...string) bool {
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
if len(exts) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, ext := range exts {
|
||||
if len(path) <= len(ext) {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(ext, path[len(path)-len(ext):]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 本地路径存在
|
||||
func PathExists(path string) bool {
|
||||
_, err := os.Lstat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// 是否为本地存在的文件, 并满足后缀名
|
||||
func IsNativeFile(path string, exts ...string) bool {
|
||||
if !HasExt(path, exts...) {
|
||||
return false
|
||||
}
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !fi.IsDir()
|
||||
}
|
||||
|
||||
// 是否为本地存在的目录
|
||||
func IsNativeDir(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
||||
// 替换后缀名
|
||||
func ReplaceExt(path string, extOld, extNew string) string {
|
||||
return path[:len(path)-len(extOld)] + extNew
|
||||
}
|
||||
|
||||
// 为合法的 app 名字
|
||||
func IsValidAppName(s string) bool {
|
||||
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
|
||||
return false
|
||||
}
|
||||
for _, c := range []rune(s) {
|
||||
if c == '_' || (c >= '0' && c <= '9') || unicode.IsLetter(c) {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 为合法的包路径
|
||||
func IsValidPkgpath(s string) bool {
|
||||
if s == "" || s[0] == '_' || (s[0] >= '0' && s[0] <= '9') {
|
||||
return false
|
||||
}
|
||||
for _, c := range []rune(s) {
|
||||
if c == '_' || c == '.' || c == '/' || (c >= '0' && c <= '9') {
|
||||
continue
|
||||
}
|
||||
if unicode.IsLetter(c) {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var pkgname = s
|
||||
if i := strings.LastIndex(s, "/"); i >= 0 {
|
||||
pkgname = s[i+1:]
|
||||
}
|
||||
return IsValidAppName(pkgname)
|
||||
}
|
||||
|
||||
// 是标准库路径
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package appbase
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHasExt(t *testing.T) {
|
||||
var tests = []struct {
|
||||
path string
|
||||
exts []string
|
||||
ok bool
|
||||
}{
|
||||
{"", nil, false},
|
||||
{"a", nil, true},
|
||||
|
||||
{"a.wa", []string{".wa"}, true},
|
||||
{"a.wa", []string{".WA"}, true},
|
||||
{"a.Wa", []string{".wa"}, true},
|
||||
|
||||
{"a.wa", []string{".wa", ".wz"}, true},
|
||||
|
||||
{".wa", []string{".wa"}, false},
|
||||
{".wa", []string{}, true},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
expect := tt.ok
|
||||
got := HasExt(tt.path, tt.exts...)
|
||||
if expect != got {
|
||||
t.Fatalf("%d: expect = %v, got = %v; // %v", i, expect, got, tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appbuild
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"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"
|
||||
)
|
||||
|
||||
var CmdBuild = &cli.Command{
|
||||
Name: "build",
|
||||
Usage: "compile Wa source code",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_output(),
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
appbase.MakeFlag_ld_stack_size(),
|
||||
appbase.MakeFlag_ld_max_memory(),
|
||||
},
|
||||
Action: CmdBuildAction,
|
||||
}
|
||||
|
||||
func CmdBuildAction(c *cli.Context) error {
|
||||
input := c.Args().First()
|
||||
outfile := ""
|
||||
|
||||
if input == "" {
|
||||
input, _ = os.Getwd()
|
||||
}
|
||||
|
||||
var opt = appbase.BuildOptions(c)
|
||||
_, err := BuildApp(opt, input, outfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func BuildApp(opt *appbase.Option, input, outfile string) (wasmBytes []byte, err error) {
|
||||
// 路径是否存在
|
||||
if !appbase.PathExists(input) {
|
||||
fmt.Printf("%q not found\n", input)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 输出参数是否合法, 必须是 wasm
|
||||
if outfile != "" && !appbase.HasExt(outfile, ".wasm") {
|
||||
fmt.Printf("%q is not valid output path\n", outfile)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 已经是 wasm, 直接返回
|
||||
if appbase.HasExt(input, ".wasm") {
|
||||
return os.ReadFile(input)
|
||||
}
|
||||
|
||||
// 只编译 wat 文件, 输出路径相同, 后缀名调整
|
||||
if appbase.HasExt(input, ".wat") {
|
||||
// 设置默认输出目标
|
||||
if outfile == "" {
|
||||
outfile = appbase.ReplaceExt(input, ".wat", ".wasm")
|
||||
}
|
||||
|
||||
watData, err := os.ReadFile(input)
|
||||
if err != nil {
|
||||
fmt.Printf("read %s failed: %v\n", input, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
wasmBytes, err := wabt.Wat2Wasm(watData)
|
||||
if err != nil {
|
||||
fmt.Printf("wat2wasm %s failed: %v\n", input, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 写到文件
|
||||
err = os.WriteFile(outfile, wasmBytes, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("write %s failed: %v\n", outfile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// OK
|
||||
return wasmBytes, nil
|
||||
}
|
||||
|
||||
// 只编译 wa/wz 文件, 输出路径相同, 后缀名调整
|
||||
if appbase.HasExt(input, ".wa", ".wz") {
|
||||
_, watOutput, err := buildWat(opt, input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 设置默认输出目标
|
||||
if outfile == "" {
|
||||
outfile = appbase.ReplaceExt(input, ".wa", ".wasm")
|
||||
}
|
||||
|
||||
// wat 写到文件
|
||||
watOutfile := appbase.ReplaceExt(outfile, ".wasm", ".wat")
|
||||
err = os.WriteFile(watOutfile, watOutput, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("write %s failed: %v\n", outfile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wat 编译为 wasm
|
||||
wasmBytes, err := wabt.Wat2Wasm(watOutput)
|
||||
if err != nil {
|
||||
fmt.Printf("wat2wasm %s failed: %v\n", input, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wasm 写到文件
|
||||
err = os.WriteFile(outfile, wasmBytes, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("write %s failed: %v\n", outfile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// OK
|
||||
return wasmBytes, nil
|
||||
}
|
||||
|
||||
// 构建目录
|
||||
{
|
||||
if !appbase.IsNativeDir(input) {
|
||||
fmt.Printf("%q is not valid output path\n", outfile)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 尝试读取模块信息
|
||||
manifest, err := config.LoadManifest(nil, input)
|
||||
if err != nil {
|
||||
fmt.Printf("%q is invalid wa moudle\n", input)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := manifest.Valid(); err != nil {
|
||||
fmt.Printf("%q is invalid wa module; %v\n", input, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if outfile == "" {
|
||||
if !manifest.IsStd {
|
||||
outfile = filepath.Join(manifest.Root, "output", manifest.Pkg.Name) + ".wasm"
|
||||
os.MkdirAll(filepath.Join(manifest.Root, "output"), 0777)
|
||||
} else {
|
||||
outfile = "a.out." + manifest.Pkg.Name + ".wasm"
|
||||
}
|
||||
}
|
||||
|
||||
// 编译出 wat 文件
|
||||
_, watOutput, err := buildWat(opt, input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wat 写到文件
|
||||
watOutfile := appbase.ReplaceExt(outfile, ".wasm", ".wat")
|
||||
err = os.WriteFile(watOutfile, watOutput, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("write %s failed: %v\n", outfile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wat 编译为 wasm
|
||||
wasmBytes, err := wabt.Wat2Wasm(watOutput)
|
||||
if err != nil {
|
||||
fmt.Printf("wat2wasm %s failed: %v\n", input, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wasm 写到文件
|
||||
err = os.WriteFile(outfile, wasmBytes, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("write %s failed: %v\n", outfile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// OK
|
||||
return wasmBytes, nil
|
||||
}
|
||||
}
|
||||
|
||||
func buildWat(opt *appbase.Option, filename string) (*loader.Program, []byte, error) {
|
||||
cfg := opt.Config()
|
||||
prog, err := loader.LoadProgram(cfg, filename)
|
||||
if err != nil {
|
||||
return prog, nil, err
|
||||
}
|
||||
|
||||
output, err := compiler_wat.New().Compile(prog, "main")
|
||||
|
||||
if err != nil {
|
||||
return prog, nil, err
|
||||
}
|
||||
|
||||
return prog, []byte(output), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appcir
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/backends/compiler_c"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
)
|
||||
|
||||
var CmdCir = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "cir",
|
||||
Usage: "print cir code",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_output(),
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
opt := appbase.BuildOptions(c)
|
||||
err := PrintCIR(opt, c.Args().First())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func PrintCIR(opt *appbase.Option, filename string) error {
|
||||
cfg := 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
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package appdev
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/api"
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
)
|
||||
|
||||
var CmdDev = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "debug",
|
||||
Usage: "only for dev/debug",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "hello",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "count-code-lines",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("count-code-lines") {
|
||||
RunCountCodeLines()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if c.Bool("hello") {
|
||||
wat, err := api.BuildFile(
|
||||
config.DefaultConfig(),
|
||||
"hello.wa", "func main() { println(123) }",
|
||||
)
|
||||
if err != nil {
|
||||
if len(wat) != 0 {
|
||||
fmt.Println(string(wat))
|
||||
}
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(wat))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
fmt.Println("...dev...")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func RunCountCodeLines() {
|
||||
var dir = "."
|
||||
if len(os.Args) > 1 {
|
||||
dir = os.Args[1]
|
||||
}
|
||||
|
||||
total := 0
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Fatal("filepath.Walk: ", err)
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal("ioutil.ReadFile: ", err)
|
||||
}
|
||||
if needSkip(path, data) {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := countLine(data)
|
||||
fmt.Println(path, n)
|
||||
|
||||
total += n
|
||||
return nil
|
||||
})
|
||||
fmt.Printf("total %d\n", total)
|
||||
}
|
||||
|
||||
func needSkip(path string, data []byte) bool {
|
||||
if !hasExt(path, ".wa", ".go", ".ws") {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(path, "3rdparty") {
|
||||
return true
|
||||
}
|
||||
if bytes.Contains(data, []byte("The Go Authors")) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func countLine(data []byte) int {
|
||||
return bytes.Count(data, []byte("\n"))
|
||||
}
|
||||
|
||||
func hasExt(name string, extensions ...string) bool {
|
||||
for _, ext := range extensions {
|
||||
if strings.HasSuffix(name, ext) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appdoc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
)
|
||||
|
||||
var CmdDoc = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "doc",
|
||||
Usage: "show documentation for package or symbol",
|
||||
ArgsUsage: "name...",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
opt := appbase.BuildOptions(c)
|
||||
|
||||
var pkgpath = "."
|
||||
var names []string
|
||||
if c.Args().Len() > 0 {
|
||||
pkgpath = c.Args().First()
|
||||
}
|
||||
if c.Args().Len() > 1 {
|
||||
names = c.Args().Slice()[1:]
|
||||
}
|
||||
|
||||
RunDoc(opt.Config(), pkgpath, names...)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func RunDoc(cfg *config.Config, pkgpath string, names ...string) {
|
||||
prog, err := loader.LoadProgram(cfg, pkgpath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
doc := BuildPkgDoc(prog)
|
||||
doc.Show(names...)
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appdoc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
"wa-lang.org/wa/internal/types"
|
||||
)
|
||||
|
||||
type PkgDoc struct {
|
||||
Prog *loader.Program
|
||||
Consts []*types.Const
|
||||
Types []*types.TypeName
|
||||
Globals []*types.Var
|
||||
Funcs []*types.Func
|
||||
}
|
||||
|
||||
func BuildPkgDoc(prog *loader.Program) (p *PkgDoc) {
|
||||
p = &PkgDoc{Prog: prog}
|
||||
p.buildDoc()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PkgDoc) Show(names ...string) {
|
||||
if len(names) != 0 {
|
||||
fmt.Println("TODO:", names)
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range p.Consts {
|
||||
// typ := c.Type().(*types.Basic)
|
||||
fmt.Printf("const %s %v\n", c.Name(), c.Type())
|
||||
}
|
||||
for _, c := range p.Globals {
|
||||
fmt.Printf("global %s %v\n", c.Name(), c.Type())
|
||||
}
|
||||
for _, fn := range p.Funcs {
|
||||
sig := fn.Type().(*types.Signature)
|
||||
if sig.Results().Len() > 0 {
|
||||
fmt.Printf("func %s%v => %v\n", fn.Name(), sig.Params(), sig.Results())
|
||||
} else {
|
||||
fmt.Printf("func %s%v\n", fn.Name(), sig.Params())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PkgDoc) buildDoc() {
|
||||
mainPkg := p.Prog.Pkgs[p.Prog.Manifest.MainPkg]
|
||||
scope := mainPkg.Pkg.Scope()
|
||||
|
||||
for _, s := range scope.Names() {
|
||||
if obj := scope.Lookup(s); obj.Exported() {
|
||||
switch x := obj.(type) {
|
||||
case *types.Const:
|
||||
p.Consts = append(p.Consts, x)
|
||||
case *types.TypeName:
|
||||
p.Types = append(p.Types, x)
|
||||
case *types.Var:
|
||||
p.Globals = append(p.Globals, x)
|
||||
case *types.Func:
|
||||
p.Funcs = append(p.Funcs, x)
|
||||
|
||||
default:
|
||||
log.Fatalf("unknown %[1]T %[1]v\n", obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appfmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
"wa-lang.org/wa/internal/format"
|
||||
)
|
||||
|
||||
var CmdFmt = &cli.Command{
|
||||
Name: "fmt",
|
||||
Usage: "format Wa source code file",
|
||||
ArgsUsage: "[<file.wa>|<path>|<path>/...]",
|
||||
Action: func(c *cli.Context) error {
|
||||
for _, path := range c.Args().Slice() {
|
||||
if err := Fmt(path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Fmt(path string) error {
|
||||
if appbase.IsNativeFile(path, ".wa", ".wz") {
|
||||
_, err := fmtFile(path)
|
||||
return err
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
if _, err := os.Lstat(config.WaModFile); err == nil {
|
||||
path = "./..."
|
||||
} else if _, err := os.Lstat(config.WaModFile + ".json"); err == nil {
|
||||
path = "./..."
|
||||
} else {
|
||||
path = "."
|
||||
}
|
||||
}
|
||||
if !filepath.IsAbs(path) && !strings.HasPrefix(path, ".") {
|
||||
return fmt.Errorf("%q is not valid path", path)
|
||||
}
|
||||
|
||||
var waFileList []string
|
||||
switch {
|
||||
case strings.HasSuffix(path, ".wa"):
|
||||
waFileList = append(waFileList, path)
|
||||
case strings.HasSuffix(path, ".wz"):
|
||||
waFileList = append(waFileList, path)
|
||||
case strings.HasSuffix(path, "/..."):
|
||||
waFileList = getDirWaFileList(
|
||||
strings.TrimSuffix(path, "..."),
|
||||
true, ".wa", ".wz", // 包含子目录
|
||||
)
|
||||
default:
|
||||
// 不包含子目录
|
||||
waFileList = getDirWaFileList(
|
||||
path, false, ".wa", ".wz",
|
||||
)
|
||||
}
|
||||
|
||||
var changedFileList []string
|
||||
for _, s := range waFileList {
|
||||
changed, err := fmtFile(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", s, err)
|
||||
}
|
||||
if changed {
|
||||
changedFileList = append(changedFileList, s)
|
||||
}
|
||||
}
|
||||
for _, s := range changedFileList {
|
||||
fmt.Println(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fmtFile(path string) (changed bool, err error) {
|
||||
code, changed, err := format.File(nil, path, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if changed {
|
||||
if err = os.WriteFile(path, code, 0666); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
func getDirWaFileList(dir string, walkSubDir bool, extList ...string) []string {
|
||||
var waFileList []string
|
||||
if !walkSubDir {
|
||||
files, err := os.ReadDir(".")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
for _, ext := range extList {
|
||||
if strings.HasSuffix(file.Name(), ext) {
|
||||
waFileList = append(waFileList, filepath.Join(dir, file.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(waFileList)
|
||||
return waFileList
|
||||
}
|
||||
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
for _, ext := range extList {
|
||||
if strings.HasSuffix(path, ext) {
|
||||
waFileList = append(waFileList, path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
sort.Strings(waFileList)
|
||||
return waFileList
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
package appinit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -10,13 +10,55 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
"wa-lang.org/wa/internal/waroot"
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/waroot"
|
||||
)
|
||||
|
||||
func (p *App) InitApp(name, pkgpath string, update bool) error {
|
||||
var CmdInit = &cli.Command{
|
||||
Name: "init",
|
||||
Usage: "init a sketch Wa module",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "set app name",
|
||||
Value: "hello",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pkgpath",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "set pkgpath file",
|
||||
Value: "myapp",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "update",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "update example",
|
||||
},
|
||||
},
|
||||
|
||||
Action: func(c *cli.Context) error {
|
||||
err := InitApp(c.String("name"), c.String("pkgpath"), c.Bool("update"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func InitApp(name, pkgpath string, update bool) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("init failed: <%s> is empty", name)
|
||||
}
|
||||
if !appbase.IsValidAppName(name) {
|
||||
return fmt.Errorf("init failed: <%s> is invalid name", name)
|
||||
}
|
||||
|
||||
if !appbase.IsValidPkgpath(pkgpath) {
|
||||
return fmt.Errorf("init failed: <%s> is invalid pkgpath", pkgpath)
|
||||
}
|
||||
|
||||
if !update {
|
||||
if _, err := os.Lstat(name); err == nil {
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package applex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/scanner"
|
||||
"wa-lang.org/wa/internal/token"
|
||||
)
|
||||
|
||||
var CmdLex = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "lex",
|
||||
Usage: "lex Wa source code and print token list",
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := Lex(c.Args().First())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Lex(filename string) error {
|
||||
if !appbase.IsNativeFile(filename, ".wa", ".wz") {
|
||||
return fmt.Errorf("%q is not valid path", filename)
|
||||
}
|
||||
|
||||
src, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var s scanner.Scanner
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile(filename, fset.Base(), len(src))
|
||||
s.Init(file, src, nil, scanner.ScanComments)
|
||||
|
||||
for {
|
||||
pos, tok, lit := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
package appllvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -9,12 +9,59 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/backends/compiler_llvm"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
)
|
||||
|
||||
func (p *App) LLVM(infile string, outfile string, target string, debug bool) error {
|
||||
cfg := p.opt.Config()
|
||||
var CmdNative = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "native",
|
||||
Usage: "compile Wa source code to native executable",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_output(),
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "dump orginal intermediate representation",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "clang",
|
||||
Usage: "set llvm/clang path",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "llc",
|
||||
Usage: "set llvm/llc path",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
outfile := c.String("output")
|
||||
target := c.String("target")
|
||||
debug := c.Bool("debug")
|
||||
infile := ""
|
||||
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
infile = c.Args().First()
|
||||
|
||||
opt := appbase.BuildOptions(c, config.WaBackend_llvm)
|
||||
if err := LLVMRun(opt, infile, outfile, target, debug); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func LLVMRun(opt *appbase.Option, infile string, outfile string, target string, debug bool) error {
|
||||
cfg := opt.Config()
|
||||
|
||||
instat, err := os.Stat(infile)
|
||||
if err != nil {
|
||||
|
|
@ -75,7 +122,7 @@ func (p *App) LLVM(infile string, outfile string, target string, debug bool) err
|
|||
llc = append(llc, "-mcpu=atmega328")
|
||||
default:
|
||||
}
|
||||
cmd0 := exec.Command(p.opt.Llc, llc...)
|
||||
cmd0 := exec.Command(opt.Llc, llc...)
|
||||
cmd0.Stderr = os.Stderr
|
||||
if err := cmd0.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "**** failed to invoke LLVM ****\n")
|
||||
|
|
@ -116,10 +163,10 @@ func (p *App) LLVM(infile string, outfile string, target string, debug bool) err
|
|||
if target != "" {
|
||||
clangArgs = append(clangArgs, "-target", target)
|
||||
}
|
||||
if p.opt.Debug {
|
||||
if opt.Debug {
|
||||
clangArgs = append(clangArgs, "-v")
|
||||
}
|
||||
cmd1 := exec.Command(p.opt.Clang, clangArgs...)
|
||||
cmd1 := exec.Command(opt.Clang, clangArgs...)
|
||||
cmd1.Stderr = os.Stderr
|
||||
cmd1.Stdout = os.Stdout
|
||||
if err := cmd1.Run(); err != nil {
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package applogo
|
||||
|
||||
import "wa-lang.org/wa/internal/3rdparty/cli"
|
||||
|
||||
var CmdLogo = &cli.Command{
|
||||
Name: "logo",
|
||||
Usage: "print Wa text format logo",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "more",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "print more text logos",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "svg",
|
||||
Usage: "print svg logo",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("svg") {
|
||||
PrintLogoSvg()
|
||||
return nil
|
||||
}
|
||||
PrintLogo(c.Bool("more"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
// 版权 @2019 凹语言 作者。保留所有权利。
|
||||
|
||||
package app
|
||||
package applogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func PrintLogoSvg() {
|
||||
fmt.Print(logoSvg)
|
||||
}
|
||||
|
||||
func PrintLogo(more bool) {
|
||||
if is1024Day() {
|
||||
fmt.Print(logo1024)
|
||||
|
|
@ -45,6 +49,39 @@ const logo1024 = `
|
|||
+------------+
|
||||
`
|
||||
|
||||
const logoSvg = `<!--
|
||||
// 版权 @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>
|
||||
`
|
||||
|
||||
const moreLogos = `
|
||||
+---+ +---+
|
||||
| * | | * |
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package applsp
|
||||
|
||||
import (
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/lsp"
|
||||
)
|
||||
|
||||
var CmdLsp = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "lsp",
|
||||
Usage: "run Wa langugage server",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "set log file",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
lsp.NewLSPServer(&lsp.Option{}).Run()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<!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>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
// 版权 @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>
|
||||
|
After Width: | Height: | Size: 628 B |
|
|
@ -0,0 +1,3 @@
|
|||
build:
|
||||
cat codemirror.js codemirror-wa.js playground.js | uglifyjs > playground-full.js && \
|
||||
cat style.css codemirror.css codemirror-*.css > playground-full.css
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
|
||||
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; }
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// 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");
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
/* 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; }
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* 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);
|
||||
|
|
@ -0,0 +1,656 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
// 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, '&');
|
||||
m = m.replace(/</g, '<');
|
||||
m = m.replace(/>/g, '>');
|
||||
|
||||
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;
|
||||
})();
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appplay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
)
|
||||
|
||||
var CmdPlay = &cli.Command{
|
||||
Name: "play",
|
||||
Usage: "start Wa playground",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "http",
|
||||
Value: ":2023",
|
||||
Usage: "set http address",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
err := RunPlayground(c.String("http"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 版权 @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)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 版权 @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
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appplay
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"wa-lang.org/wa/api"
|
||||
"wa-lang.org/wa/internal/wamime"
|
||||
)
|
||||
|
||||
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) {
|
||||
filename := "prog.wa"
|
||||
if wamime.GetCodeMime(filename, code) == "wz" {
|
||||
filename = "prog.wz"
|
||||
}
|
||||
|
||||
output, err := api.FormatCode(filename, string(code))
|
||||
if err != nil {
|
||||
resp := &fmtResponse{
|
||||
Error: err.Error(),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp := &fmtResponse{
|
||||
Body: string(output),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appplay
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/api"
|
||||
"wa-lang.org/wa/internal/wamime"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
filename := "prog.wa"
|
||||
if wamime.GetCodeMime(filename, []byte(req.Body)) == "wz" {
|
||||
filename = "prog.wz"
|
||||
}
|
||||
|
||||
result, err := api.RunCode(api.DefaultConfig(), filename, req.Body)
|
||||
if err != nil {
|
||||
resp := &Response{Errors: err.Error()}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp := &Response{
|
||||
Events: []Event{
|
||||
{
|
||||
Message: string(result),
|
||||
Kind: "stdout",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 版权 @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
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package apprun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/app/appbuild"
|
||||
"wa-lang.org/wa/internal/wazero"
|
||||
)
|
||||
|
||||
var CmdRun = &cli.Command{
|
||||
Name: "run",
|
||||
Usage: "compile and run Wa program",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
},
|
||||
Action: CmdRunAction,
|
||||
}
|
||||
|
||||
func CmdRunAction(c *cli.Context) error {
|
||||
input := c.Args().First()
|
||||
outfile := ""
|
||||
|
||||
if input == "" {
|
||||
input, _ = os.Getwd()
|
||||
}
|
||||
|
||||
var opt = appbase.BuildOptions(c)
|
||||
wasmBytes, err := appbuild.BuildApp(opt, input, outfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var appArgs []string
|
||||
if c.NArg() > 1 {
|
||||
appArgs = c.Args().Slice()[1:]
|
||||
}
|
||||
|
||||
stdout, stderr, err := wazero.RunWasm(opt.Config(), input, wasmBytes, appArgs...)
|
||||
if err != nil {
|
||||
if len(stdout) > 0 {
|
||||
fmt.Fprint(os.Stdout, string(stdout))
|
||||
}
|
||||
if len(stderr) > 0 {
|
||||
fmt.Fprint(os.Stderr, string(stderr))
|
||||
}
|
||||
if exitCode, ok := wazero.AsExitError(err); ok {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
fmt.Println(err)
|
||||
}
|
||||
if len(stdout) > 0 {
|
||||
fmt.Fprint(os.Stdout, string(stdout))
|
||||
}
|
||||
if len(stderr) > 0 {
|
||||
fmt.Fprint(os.Stderr, string(stderr))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package appssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"wa-lang.org/wa/internal/loader"
|
||||
"wa-lang.org/wa/internal/ssa"
|
||||
)
|
||||
|
||||
var CmdSsa = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "ssa",
|
||||
Usage: "print Wa ssa code",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
opt := appbase.BuildOptions(c)
|
||||
err := SSARun(opt, c.Args().First())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func SSARun(opt *appbase.Option, filename string) error {
|
||||
cfg := opt.Config()
|
||||
prog, err := loader.LoadProgram(cfg, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog.SSAMainPkg.WriteTo(os.Stdout)
|
||||
|
||||
var funcNames []string
|
||||
for name, x := range prog.SSAMainPkg.Members {
|
||||
if _, ok := x.(*ssa.Function); ok {
|
||||
funcNames = append(funcNames, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(funcNames)
|
||||
for _, s := range funcNames {
|
||||
prog.SSAMainPkg.Func(s).WriteTo(os.Stdout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package apptest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/app/appbase"
|
||||
"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"
|
||||
"wa-lang.org/wa/waroot"
|
||||
)
|
||||
|
||||
var CmdTest = &cli.Command{
|
||||
Name: "test",
|
||||
Usage: "test Wa packages",
|
||||
Flags: []cli.Flag{
|
||||
appbase.MakeFlag_target(),
|
||||
appbase.MakeFlag_tags(),
|
||||
&cli.StringFlag{
|
||||
Name: "run",
|
||||
Usage: "set run func file name pattern",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
opt := appbase.BuildOptions(c)
|
||||
|
||||
var pkgpath = "."
|
||||
var runPattern = c.String("run")
|
||||
var appArgs []string
|
||||
|
||||
if c.Args().Len() > 0 {
|
||||
pkgpath = c.Args().First()
|
||||
}
|
||||
if c.Args().Len() > 1 {
|
||||
appArgs = c.Args().Slice()[1:]
|
||||
}
|
||||
|
||||
RunTest(opt.Config(), pkgpath, runPattern, appArgs...)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func RunTest(cfg *config.Config, pkgpath, runPattern string, appArgs ...string) {
|
||||
var pkgList = []string{pkgpath}
|
||||
if pkgpath == "std" {
|
||||
pkgList = waroot.GetStdPkgTestList()
|
||||
}
|
||||
for _, p := range pkgList {
|
||||
runTest(cfg, p, runPattern, appArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(cfg *config.Config, pkgpath, runPattern string, appArgs ...string) {
|
||||
cfg.UnitTest = true
|
||||
prog, err := loader.LoadProgram(cfg, pkgpath)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 生成 wat 文件(main 函数为空)
|
||||
watOutput, err := compiler_wat.New().Compile(prog, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// wat 落盘(仅用于调试)
|
||||
os.WriteFile("a.out.wat", []byte(watOutput), 0666)
|
||||
|
||||
// 编译为 wasm
|
||||
wasmBytes, err := wabt.Wat2Wasm([]byte(watOutput))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
m, err := wazero.BuildModule(cfg, wasmName, wasmBytes, wasmArgs...)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer m.Close()
|
||||
|
||||
// 执行测试函数
|
||||
var firstError error
|
||||
for i := 0; i < len(mainPkg.TestInfo.Tests); i++ {
|
||||
t := mainPkg.TestInfo.Tests[i]
|
||||
|
||||
if runPattern != "" {
|
||||
matched, err := filepath.Match(runPattern, t.Name)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tFuncName := mainPkg.Pkg.Path() + "." + t.Name
|
||||
tFuncName = strings.ReplaceAll(tFuncName, "/", "$")
|
||||
_, stdout, stderr, err := m.RunFunc(tFuncName)
|
||||
if t.OutputPanic {
|
||||
stdout = fmtGotOutput(stdout)
|
||||
expect, got := t.Output, string(stdout)
|
||||
|
||||
if exitCode, _ := wazero.AsExitError(err); exitCode == 0 {
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect panic, got = nil\n")
|
||||
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect(panic) = %q, got = %q", expect, "nil")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(got, "panic: "+expect) { // panic: ${expect} (pos)
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect(panic) = %q, got = %q\n", expect, got)
|
||||
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect(panic) = %q, got = %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载
|
||||
{
|
||||
m, err = wazero.BuildModule(cfg, wasmName, wasmBytes, wasmArgs...)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 临时方案: defer 太多
|
||||
defer m.Close()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
if s := sWithPrefix(err.Error(), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
stdout = fmtGotOutput(stdout)
|
||||
if t.Output != "" && t.Output == string(stdout) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
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)
|
||||
}
|
||||
if s := sWithPrefix(string(stderr), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Output != "" {
|
||||
if expect, got := t.Output, string(stdout); expect != got {
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect = %q, got = %q", expect, got)
|
||||
}
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect = %q, got = %q\n", expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(mainPkg.TestInfo.Examples); i++ {
|
||||
t := mainPkg.TestInfo.Examples[i]
|
||||
|
||||
if runPattern != "" {
|
||||
matched, err := filepath.Match(runPattern, t.Name)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tFuncName := mainPkg.Pkg.Path() + "." + t.Name
|
||||
tFuncName = strings.ReplaceAll(tFuncName, "/", "$")
|
||||
_, stdout, stderr, err := m.RunFunc(tFuncName)
|
||||
if t.OutputPanic {
|
||||
stdout = fmtGotOutput(stdout)
|
||||
expect, got := t.Output, string(stdout)
|
||||
|
||||
if exitCode, _ := wazero.AsExitError(err); exitCode == 0 {
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect panic, got = nil\n")
|
||||
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect panic, got = nil")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(got, "panic: "+expect) { // panic: ${expect} (pos)
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect(panic) = %q, got = %q\n", expect, got)
|
||||
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect(panic) = %q, got = %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载
|
||||
{
|
||||
m, err = wazero.BuildModule(cfg, wasmName, wasmBytes, wasmArgs...)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 临时方案: defer 太多
|
||||
defer m.Close()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
stdout = fmtGotOutput(stdout)
|
||||
if t.Output != "" && t.Output == string(stdout) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
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)
|
||||
}
|
||||
if s := sWithPrefix(string(stderr), " "); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Output != "" {
|
||||
if expect, got := t.Output, string(stdout); expect != got {
|
||||
if firstError == nil {
|
||||
firstError = fmt.Errorf("expect = %q, got = %q", expect, got)
|
||||
}
|
||||
fmt.Printf("---- %s.%s\n", prog.Manifest.MainPkg, t.Name)
|
||||
fmt.Printf(" expect = %q, got = %q\n", expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
if firstError != nil {
|
||||
fmt.Printf("FAIL %s %v\n", prog.Manifest.MainPkg, time.Now().Sub(startTime).Round(time.Millisecond))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("ok %s %v\n", prog.Manifest.MainPkg, time.Now().Sub(startTime).Round(time.Millisecond))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func sWithPrefix(s, prefix string) string {
|
||||
lines := strings.Split(strings.TrimSpace(s), "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = prefix + strings.TrimSpace(line)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func fmtGotOutput(stdout []byte) []byte {
|
||||
stdout = bytes.TrimSpace(stdout)
|
||||
lines := bytes.Split(stdout, []byte("\n"))
|
||||
for i, s := range lines {
|
||||
lines[i] = bytes.TrimSpace(s)
|
||||
}
|
||||
return bytes.Join(lines, []byte("\n"))
|
||||
}
|
||||