WebRacket 语言是 Racket 的一个子集,它编译到 WebAssembly。
The WebRacket language is a subset of Racket that compiles to WebAssembly

原始链接: https://github.com/soegaard/webracket

## WebRacket:浏览器中的 Racket WebRacket 是一个编译器,将 Racket 编程语言的一个子集翻译成 WebAssembly (wasm),从而使 Racket 代码可以直接在 Web 浏览器(和 Node.js)中运行。虽然完全支持 Racket 是最终目标,但目前的实现已经足够稳健,可以构建实用的 Web 应用程序,而无需 JavaScript。 主要特性包括一个 JavaScript 外函数接口 (FFI),提供对浏览器 API 的访问,例如 DOM、Canvas、MathJax、XTermJS 和 JSXGraph。该编译器利用标准的 Racket 扩展器,支持许多语法形式,但目前不支持模块和协程。 WebRacket 优先考虑与广泛支持的 WebAssembly 特性的兼容性,以实现广泛的浏览器支持(Chrome、Firefox、Safari)。开发重点是修复错误和支持模块,并计划添加复数和任意精度整数。 要开始使用,您需要 wasm-tools、Node.js、Racket 9.0 和 raco-static-web。示例包括 MathJax 编辑器、矩阵数字雨演示和太空侵略者游戏,展示了 WebRacket 的功能。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 WebRacket 语言是 Racket 的一个子集,可以编译为 WebAssembly (github.com/soegaard) 15 分,mfru 发表于 2 小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 dfajgljsldkjag 发表于 0 分钟前 [–] 看到又一种语言以 WebAssembly 为目标很有意思,特别是像 Racket 这样的语言。 编译为语言子集的事实目前限制了它的实用性。 我认为这是一个不错的概念验证,但它需要完整的语言支持。回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

The WebRacket language is a subset of Racket that compiles to WebAssembly (wasm).

The long-term goal is to support full Racket. However, to quote Piet Hein, “Things take time.”

The subset supported by the WebRacket compiler is large enough, to enable programmers to build practical programs for the web.

The generated WebAssembly can be run either in the terminal (via Node) or in the browser. The browser is the main focus. WebAssembly is somewhat of a moving target. The compiler only uses widely supported features of WebAssembly. Expect the generated code to work in Chrome, Firefox and Safari.

A JavaScript FFI makes it possible to use standard JavaScript functions as well as browser specific APIs. Included are bindings for the DOM, Canvas, MathJax, XTermJS and JSXGraph.

The hope is that this project allows the Racket community to experiment with WebAssembly. The ideal outcome is that the experience can be used to extend the normal Racket compiler with a WebAssembly backend. In the meantime, we can have fun writing Racket programs that run in the browser.

If you want to develop Racket programs that run in the browser and want to avoid JavaScript, then WebRacket is for you.

The FFI allows you to use WebRacket functions as event callbacks on the JavaScript side.

See examples/ for a few WebRacket projects.

The WebRacket compiler accepts a file containing a top-level form as input. The module form is not supported. However, include is available.

For supported data types most functions in racket/base and racket are available as primitives. Most are implemented directly in WebAssembly, some are reimplemented in WebRacket.

Most basic data types are implemented, some have restrictions.

The numerical tower contains only flonums and fixnums. Complex numbers and bignums are missing.

Mutable hash tables of all four varieties (eq? eqv? equal? always?) are supported. The values of all mutable hash tables are strongly held, even for tables created by the weak construtors.

Immutable hash tables are not yet supported.

No direct support for regular expressions at the moment. These will materialize once support for linklets and modules improves.

Since the main target is the browser, only string (and byte string) ports are supported. If there is interest for file ports (for the terminal), let me know.

Most structure related bells and whistles are implemented including super structures, structure properties and applicable structures. The most notable missing feature is prefab structures.

The WebRacket compiler uses the standard Racket expander, so a large number of syntactic forms are supported. This includes for and match.

Most notable omissions are the forms module, module* and with-continuation-mark.

Tail calls are supported. Multiple values are supported. Upward flowing exceptions are supported.

Continuations and continuation marks are not supported. In particular, there is no support for call/cc (sorry Shriram).

Other omissions: promises, breaks, exit and black box.

Concurrency and Parallelism

Single threaded for now.

Foreign Function Interface

The JavaScript foreign function interface is used to access browser functionality. The hope is that the community will help write bindings for commonly used libraries. To some degree the generation of foreign function interfaces can be automated with the help of an LLM.

Included bindings currently cover the Math, DOM, Canvas, MathJax, XTermJS, and JSXGraph.

After the initial release, the focus is to fix bugs found by early adopters.

Then the top priority is to support modules. Work on implementing linklets (needed to support modules) have already started.

Due to my personal interests, complex numbers and bignums are likely to appear sooner rather than later.

Impersonators and chaperones are needed to support contracts.

Unlocking modules (and linklets) will also unlock the full implementation of regular expressions present in the source of the Racket expander.

Support for continuations and continuation marks, although high on the wish list, is something that is trickier to implement given the nature of the target. Last resort is to add a CPS pass to the compiler.

You need:

  • wasm-tools from the Bytecode Alliance (version 1.243.0 or newer)
  • Node.js (recent version; needs to support --experimental-wasm-exnref)
  • Racket 9.0
  • raco-static-web
  • a clone of the webracket repo

The WebRacket compiler depends on two external programs: wasm-tools and node.

The wasm-tools project by Bytecode Alliance consists of a suite of WebAssembly tools. WebRacket uses the wasm-tools to compile WebAssembly source files (.wat) (in S-expression format) to bytecode (.wasm).

Node (or Node.js) is a JavaScript runtime environment used to run JavaScript programs in the terminal. WebRacket uses Node to run programs directly in the terminal. This is useful for testing programs that do not depend on browser functionality. A relatively new version is needed.

When the target is the browser, the WebRacket compiler can optionally produce an html file that loads the generated WebAssembly program. In order to test locally, the package raco-static-web by Sam Philips is very convenient.

  1. Download the latest release from:

    https://github.com/bytecodealliance/wasm-tools/releases
    
  2. Unpack the tar-ball:

    tar -xvf wasm-tools-1.243.0-aarch64-macos.tar.gz
    
  3. Open README.md in the browser to see further instructions.

  4. Make sure to place wasm-tools in somewhere in your PATH. On macOS, you can do it with:

    sudo mv wasm-tools /usr/local/bin/
    
  5. Test that wasm-tools works:

Depending on security settings, you might get a dialog on macOS. If so, open the system preferences and find the "Privacy and Security" tab. Then allow wasm-tools to run

  1. Go to https://nodejs.org/en/download and follow the instructions.

  2. Test that node works in the terminal (and that it is in you path).

WebRacket needs support for exnref so your Node version needs to accept the --experimental-wasm-exnref flag.

Here is what I see, when Node is started:

% node --experimental-wasm-exnref --expose-gc
Welcome to Node.js v24.9.0.
Type ".help" for more information.

The compiler needs Racket 9 or newer.

  1. Install the web-server using raco.

    raco pkg install raco-static-web
    
  2. Test it works. Go to a folder that holds an html file. Then start the web-server with:

    Follow the printing instructions.

The WebRacket compiler is a direct-style compiler. This choice has made it easier to relate the generated code to the source program. In the future we will probably need to add a CPS-pass in order to support continuations and continuation marks.

The frontend of the WebRacket compiler uses read-syntax to read a WebRacket program from a file. The resulting syntax object is fed into the normal Racket expander to produce a program in fully expanded form.

The middle end of the compiler consists of several passes implemented using the NanoPass framework.

The passes are as follows:

unexpand
parse
flatten-topbegin
infer-names
convert-quotations
explicit-begin
explicit-case-lambda
α-rename
assignment-conversion
categorize-applications
anormalize
closure-conversion
flatten-begin
(classify-variables)
generate-code

See the comments in "compiler.rkt" for an explanation of each pass.

The code generator generates WebAssembly in the form of S-expressions in the "folded" format.

This code generator is inspired by "Destination-driven Code Generation" by Dybvig, Hieb and Butler. There are some differences, however. The code generator in the paper generates "flat" code (assembler) whereas we generate nested WebAssembly instructions.

Finally, the external tool wasm-tools parse converts the S-expression representation into bytecode format.

The main part of the compiler is in "compiler.rkt". The WebAssembly runtime is in "runtime-wasm.rkt". The standard library (implemented in WebRacket) is found in stdlib/. FFI bindings for popular libraries are in ffi/.

It has been a design goal to avoid relying on functionality provided by the WebAssembly host if possible. Who knows - maybe someone needs a non-JavaScript host at some point? For browser functionality there is no way around interfacing with the JavaScript host. The JavaScript part of the runtime support is in assembler.rkt.

The folder examples/ contains a few examples that show different aspects of WebRacket.

Examples include:

  • MathJax 4 two-pane editor/preview
  • Matrix digital rain
  • MiniScheme REPL
  • pict port
  • Space Invaders
  • xtermjs demo
  1. Go to the examples/ folder

  2. Start a local web-server

    raco static-web

  3. Open http://localhost:8000/ in your favorite browser.

  4. Click on a folder and then click the html file.

The JavaScript library MathJax allows web page authors to use mathematical formulas on their web pages.

The MathJax example allows you to experiment with the newest version, namely MathJax 4.

The page will load MathJax 4 from a CDN (content delivery network). The user is then presented with a simple two-pane editor/preview interface. Any LaTeX formula entered on the left, is rendered and displayed on the right.

The implementation is in one file "mathjax4.rkt".

Given html in the form of an S-expression, the program dynamically generates a web page with an "Input Pane" and a "Preview Pane". An event handler is attached to the input pane, such that the WebRacket function update-preview-handler is called each time there are changes in the input pane.

This example demonstrates how to use js-var and js-send to get access to the JavaScript object MathJax and how to invoke methods such as typesetPromise.

MathJax 4 example

The movie "Matrix" by the Wachowskis featured a neat effect known as "Digital Rain".

https://en.wikipedia.org/wiki/Digital_rain

The effect shows constantly falling characters, mostly green, falling down the screen.

The example uses the library xterm.js which emulates a terminal in the browser.

The demo highlights WebRacket's bindings for external JS libraries, terminal control through the XtermJS FFI, and real-time animation using browser callbacks.

Matrix - digital rain example

Improvements to this example are welcome.

MiniScheme provides an interactive Scheme REPL.

There are two parts to this example.

A small "Scheme" interpreter handles reading and evaluation of the user input. Large parts of an editor is implemented to make the repl tolerable to use. The terminal itself is backed by xterm.js.

Improvements to this example are welcome.

MiniScheme example

This is a port of the picture library pict.

Pict example

Space Invaders is a 1978 shoot'em up video game.

The example uses a 2D canvas to render the playing area. It sets up the canvas and styles entirely from WebRacket, tracks game entities with mutable structs, handles keyboard input for movement and shooting, and drives gameplay with a requestAnimationFrame loop. The game illustrates WebRacket's canvas bindings, event handling for user input, and stateful animation of sprites in a browser environment.

space invaders example

The XtermJS Demo recreates the interactive terminal shown on the xterm.js homepage.

It constructs the page layout and styling with DOM operations, initializes an xterm.js instance with theme options, registers add-ons, and routes user input to a set of built-in demo commands.

It demonstrates styling and layout via WebRacket's DOM FFI, deep terminal control through the XtermJS bindings, and integration of JS add-ons from Racket code.

xtermjs example

联系我们 contact @ memedata.com