Skip to content
On this page

凹语言开发WebAssembly应用3分钟入门


凹语言是国内首个面向WebAssembly设计通用编程语言,也是目前被 CNCF基金会wasm全景图 收录的的唯一一个来自中国的开源编程语言项目。凹语言项目诞生时的一个愿景就是简化WebAssembly网页应用的开发。这里我们将展示如何快速开启一个WebAssembly应用。

1. 安装凹语言命令行

凹语言采用Go语言实现,目前可以通过以下Go命令安装最新的凹语言命令行:

$ go install wa-lang.org/wa@master

安装完成后输出wa命令查看帮助信息:

$ wa
NAME:
   Wa - Wa is a tool for managing Wa source code.

USAGE:
   wa [global options] command [command options] [arguments...]

VERSION:
   v0.17.0

COMMANDS:
   play   start Wa playground
   init   init a sketch Wa module
   build  compile Wa source code
   run    compile and run Wa program
   fmt    format Wa source code file
   test   test Wa packages
   lsp    run Wa langugage server (dev)
   yacc   generates parsers for LALR(1) grammars
   logo   print Wa text format logo

GLOBAL OPTIONS:
   --debug, -d              set debug mode (default: false)
   --trace value, -t value  set trace mode (*|app|compiler|loader)
   --help, -h               show help (default: false)
   --version, -v            print the version (default: false)

COPYRIGHT:
   Copyright 2018 The Wa Authors. All rights reserved.

See "https://wa-lang.org" for more information.

2. 初始化凹语言工程

通过wa init命令初始化一个名为hello的工程:

$ wa init
$ cd hello
$ tree
.
├── README.md
├── output
│   └── index.html
├── src
│   └── main.wa
└── wa.mod

3 directories, 4 files

其中 wa.mod 是工程文件:

ini
$ cat wa.mod 
# 版权 @2024 hello 作者。保留所有权利。

name = "hello"
pkgpath = "myapp"
target = "js

其中 main.wa 是主程序:

wa
$ cat src/main.wa 
// 版权 @2024 hello 作者。保留所有权利。

func main {
    println("你好,凹语言!")
    println(Sum(100))
}

func Sum(n: int) => int {
    v: int
    for i := 1; i <= n; i++ {
        v += i
    }
    return v
}

此外还有一个output/index.html网页文件,该文件内容稍后说明。

3. 编译并执行凹语言程序

hello目录对应的命令行环境通过wa run命令编译并执行:

$ wa run
listen at http://localhost:8000
...

该命令在编译得到wasm文件后,会在output命令启动一个web服务。然后通过以上网址可以访问index.html页面。效果如下:

在开发者控制台窗口输出了“你好,凹语言!”和5050结果。此时output目录文件如下:

$ tree output/
output/
├── hello.js
├── hello.wasm
├── hello.wat
└── index.html

1 directory, 4 files

首先是main.wa编译得到hello.wat,然后转化为hello.wasm二进制格式。hello.jshello.wasm文件和JavaScript之间的胶水代码。

当然也可以通过增加-target=wasi参数输出wasi规范的wasm文件,并在命令行执行:

$ wa run -target=wasi
你好,凹语言!
5050

注意:凹语言的main函数对应输出的wasm文件导出的函数,并不会自动执行。因此如果使用wasmer等第三方工具执行,需要将main函数的代码移动到init函数中。

4. 看看页面输出的是什么

打开网页看到的是1+2+3+...+100 = 5050,这是在output/index.html网页调用Sum函数计算的结果。index.html文件如下:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hello</title>
</head>
<body>
  <div style="text-align: center;">
    <pre id="output"></pre>
  </div>

  <script type="text/javascript" src="./hello.js"></script>

  <script>
    let app = new WaApp();
    app.init().then((result) => {
      app.main();
      document.getElementById('output').innerText = `1+2+3+...+100 = ${app.Sum(100)}`;
    })
  </script>
</body>
</html>

首先是通过hello.js胶水代码初始化wasm模块,得到了WaApp对象。然后通过app.init()执行凹语言的init初始化代码,然后通过app.main()执行main函数代码。app.Sum对应的是导出的Sum函数,然后将计算结果更新到页面的output区域。

5. 凹语言版本的Brainfuck解释器

在 “st0013:凹语言、图灵机和 BF 语言” 文章中,我们介绍过用凹语言写了一个命令行版本的Brainfuck解释器,现在可以将其改造为网页版本。

首先是修改 main.wa 导出 Run 函数(waroot/examples/brainfuck):

import "brainfuck/bfpkg"

func main {
	// print hi
	const code = "++++++++++[>++++++++++<-]>++++.+."
	vm := bfpkg.NewBrainFuck(code)
	println(string(vm.Run()))
}

func Run(code: string) => string {
	vm := bfpkg.NewBrainFuck(code)
	return string(vm.Run())
}

然后修改 output/index.html 页面:

html
<!DOCTYPE html>
<html lang="en">
<body>
	<label for="source">Brainfuck Source</label>
	<br>
	<textarea name="source" id="source" cols="30" rows="10">++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.</textarea>
	<br>
	<button id="btn">Run</button>
	<br>
	<p>Output</p>
	<pre id="output"></pre>

	<script type="text/javascript" src="./brainfuck.js"></script>

	<script>
		let app = new WaApp();
		app.init().then((result) => {
			document.getElementById('btn').addEventListener('click', function () {
				let code = document.getElementById('source').value;
				let output = app.Run(code);
				document.getElementById('output').innerText = output;
			})
		})
	</script>
</body>
</html>

其中app.Run(code)就是调用导出的Run函数,解释执行Brainfuck代码。

在线地址: https://wa-lang.org/wa/brainfuck/,执行效果如下:

6. 展望

WebAssembly 是一个非常有前景的基础技术,但是至少使用门槛较高。凹语言作为面向WebAssembly设计的通用语言,希望通过简化流程让大家更方便地使用该技术从而享受技术发展带来的红利。也希望对该技术方向感兴趣的同学一起共建。