mirror of
https://gitee.com/wa-lang/wa.git
synced 2025-12-06 09:18:53 +08:00
214 lines
4.5 KiB
Go
214 lines
4.5 KiB
Go
// Copyright (C) 2024 武汉凹语言科技有限公司
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
//go:build wasm
|
|
// +build wasm
|
|
|
|
// wa 命令 js/wasm 版本, 用于 playground 环境.
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"syscall/js"
|
|
"testing/fstest"
|
|
"time"
|
|
|
|
"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"
|
|
"wa-lang.org/wa/internal/wat/watutil"
|
|
)
|
|
|
|
var waError error
|
|
|
|
func main() {
|
|
runMain()
|
|
}
|
|
|
|
//go:wasmexport runMain
|
|
func runMain() {
|
|
window := js.Global().Get("window")
|
|
|
|
// __WA_FILE_NAME__ 表示文件名, 用于区分中英文语法
|
|
// __WA_CODE__ 代码内容
|
|
waName := getJsValue(window, "__WA_FILE_NAME__", "hello.wa")
|
|
waCode := getJsValue(window, "__WA_CODE__", "// no code")
|
|
waVfsJson := getJsValue(window, "__WA_VFS_JSON__", "")
|
|
|
|
waClearError()
|
|
|
|
var (
|
|
outWat string
|
|
outFmt string
|
|
outWasm []byte
|
|
)
|
|
|
|
if waVfsJson == "" {
|
|
outWat = waGenerateWat(waName, waCode)
|
|
outFmt = waFormatCode(waName, waCode)
|
|
outWasm = waGenerateWasm(waName, outWat)
|
|
} else {
|
|
vfs := waLoadVFSFromJson(waVfsJson, waName)
|
|
|
|
outWat = waGenerateWatVFS(vfs, waName)
|
|
outFmt = waFormatCode(waName, waCode)
|
|
outWasm = waGenerateWasm(waName, outWat)
|
|
}
|
|
|
|
if !window.IsNull() && !window.IsUndefined() {
|
|
window.Set("__WA_WAT__", outWat)
|
|
window.Set("__WA_FMT_CODE__", outFmt)
|
|
|
|
// 复制数组到 js
|
|
jsArray := js.Global().Get("Uint8Array").New(len(outWasm))
|
|
js.CopyBytesToJS(jsArray, outWasm)
|
|
window.Set("__WA_WASM__", jsArray)
|
|
|
|
window.Set("__WA_ERROR__", waGetErrorText())
|
|
} else {
|
|
fmt.Println(outWat)
|
|
}
|
|
}
|
|
|
|
// 从 vfs json 字符串构建 vfs
|
|
func waLoadVFSFromJson(waVfsJson, filename string) *config.PkgVFS {
|
|
if waGetError() != nil {
|
|
return nil
|
|
}
|
|
|
|
// json解析为扁平的map
|
|
var m map[string]string
|
|
if err := json.Unmarshal([]byte(waVfsJson), &m); err != nil {
|
|
waSetError(err)
|
|
}
|
|
|
|
// 构建 map fs
|
|
mapfs := make(fstest.MapFS)
|
|
now := time.Now()
|
|
|
|
// 遍历 JSON 文件路径
|
|
for filePath, content := range m {
|
|
mapfs[filePath] = &fstest.MapFile{
|
|
Data: []byte(content),
|
|
Mode: 0644,
|
|
ModTime: now,
|
|
}
|
|
}
|
|
|
|
// 设置 app 文件系统
|
|
return &config.PkgVFS{
|
|
App: mapfs,
|
|
}
|
|
}
|
|
|
|
func waGenerateWasm(filename, code string) []byte {
|
|
if waGetError() != nil {
|
|
return nil
|
|
}
|
|
wasmBytes, err := watutil.Wat2Wasm(filename, []byte(code))
|
|
if err != nil {
|
|
if waGetError() == nil {
|
|
waSetError(err)
|
|
}
|
|
return nil
|
|
}
|
|
return wasmBytes
|
|
}
|
|
|
|
func waGenerateWat(filename, code string) string {
|
|
cfg := api.DefaultConfig()
|
|
cfg.Target = api.WaOS_js
|
|
|
|
wat, err := waBuildFile(cfg, filename, code)
|
|
if err != nil {
|
|
if waGetError() == nil {
|
|
waSetError(err)
|
|
}
|
|
return ""
|
|
}
|
|
return string(wat)
|
|
}
|
|
|
|
func waGenerateWatVFS(vfs *config.PkgVFS, filename string) string {
|
|
cfg := api.DefaultConfig()
|
|
cfg.Target = api.WaOS_js
|
|
|
|
wat, err := waBuildVFS(vfs, cfg, filename)
|
|
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, "loader.LoadProgramFile failed, err = %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
watOut, err := compiler_wat.New().Compile(prog)
|
|
return []byte(watOut), err
|
|
}
|
|
|
|
// 从文件系统构建 wat 目标
|
|
func waBuildVFS(vfs *config.PkgVFS, cfg *config.Config, pkgpath string) (wat []byte, err error) {
|
|
prog, err := loader.LoadProgramVFS(vfs, cfg, pkgpath)
|
|
if err != nil || prog == nil {
|
|
logger.Tracef(&config.EnableTrace_api, "loader.LoadProgramVFS failed, err = %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
watOut, err := compiler_wat.New().Compile(prog)
|
|
return []byte(watOut), err
|
|
}
|
|
|
|
func getJsValue(window js.Value, key, defaultValue string) string {
|
|
if window.IsNull() || window.IsUndefined() {
|
|
return defaultValue
|
|
}
|
|
if x := window.Get(key); x.IsNull() || x.IsUndefined() {
|
|
return defaultValue
|
|
} else {
|
|
return x.String()
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|