# WAG - The WebAssembly Code Generator This Ruby gem allows you to generate WebAssembly programs programmatically using a DSL. It is closely modeled after WAT, the WebAssembly text format, and at this stage generates WAT and compiles and validates it using [the WebAssembly Binary Toolkit][1]. Due to the flexibility of WAT this library is very flexible in what structures it allows you to create. Be aware that you can build modules which are not valid WASM. Always validate your modules by using `#to_wasm.valid?`. ## Keyword conflict Any WASM instructions whose name conflicts with a Ruby keyword (eg `loop`, `return`, etc) are also aliased with a underscore suffix for use in the DSL. The methods defining the original names are also there, so can be used by the likes of `public_send`, etc. ## Folding WAG supports generating both the "folded" and "unfolded" variants of the WAT language. As an example here are two implementations of Euclid's Greatest Common Divisor algorithm: ### Example of unfolded generation ```ruby unfolded = WAG::Module.new.build do func(:gcd) do param(:a, :i32) param(:b, :i32) result(:i32) local(:r, :i32) block loop_ # let r = a % b local.get(:a) local.get(:b) i32.rem_s local.set(:r) # let a = b and b = R local.get(:b) local.set(:a) local.get(:r) local.set(:b) # if a % b == 0, return b local.get(:a) local.get(:b) i32.rem_s i32.eqz br_if 1 br 0 end_ end_ local.get(:b) return_ end export("gcd").func(:gcd) end ``` ### Example of folded generation ```ruby folded = WAG::Module.new.build do func(:gcd) do param(:a, :i32) param(:b, :i32) result(:i32) local(:r, :i32) block do loop_ do # let r = a % b local.set(:r) do i32.rem_s do local.get(:a) local.get(:b) end end # let a = b and b = R local.set(:a) do local.get(:b) end local.set(:b) do local.get(:r) end # if a % b == 0, return b br_if(1) do i32.eqz do i32.rem_s do local.get(:a) local.get(:b) end end end br 0 end end return_ do local.get(:b) end end export("gcd") do func(:gcd) end end ``` Both modules emit identical WASM bytecode and produce the same answers: ```ruby folded.to_wasm.save("folded.wasm") unfolded.to_wasm.save("unfolded.wasm") ``` ```bash $ sha256sum folded.wasm unfolded.wasm 0023ef97eba001226401e432912f2e644a6cbef107ba183546c51177eee46e2c folded.wasm 0023ef97eba001226401e432912f2e644a6cbef107ba183546c51177eee46e2c unfolded.wasm $ wasmtime folded.wasm --invoke gcd 270 192 6 $ wasmtime unfolded.wasm --invoke gcd 270 192 6 ``` 1: https://github.com/WebAssembly/wabt ## Installation Add this line to your application's `Gemfile`: ```ruby gem 'wag' ``` And then execute: $ bundle install Or install it yourself as: $ gem install wag ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome at https://harton.dev/james/wag. ## License This software is licensed under the terms of the [HL3-FULL](https://firstdonoharm.dev), see the `LICENSE.md` file included with this package for the terms. This license actively proscribes this software being used by and for some industries, countries and activities. If your usage of this software doesn't comply with the terms of this license, then [contact me](mailto:james@harton.nz) with the details of your use-case to organise the purchase of a license - the cost of which may include a donation to a suitable charity or NGO.