wag/spec/acceptance/code_validates_spec.rb
James Harton 1076413d03
Some checks failed
continuous-integration/drone/push Build is failing
chore: migrate to local.
2023-07-28 08:32:26 +12:00

485 lines
11 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
# Simple examples taken from: https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html
RSpec.describe 'WAT code generation' do
let(:mod) { WAG::Module.new }
subject { mod }
context 'Basic example' do
let(:mod) do
super().build do
func do
result(:i32)
i32.const(42)
end
export('helloWorld') do
func(0)
end
end
end
describe '#to_wat' do
subject { mod.to_wat.to_s }
it { is_expected.to eq '(module (export "helloWorld" (func 0)) (func (result i32) (i32.const 42)))' }
end
describe '#to_wasm' do
subject { mod.to_wasm }
it { is_expected.to be_valid }
end
end
context 'Game of life example' do
let(:mod) do
super().build do
import('console', 'log') do
func(:log) do
param(:i32)
param(:i32)
end
end
memory(:mem, 1)
table(16, :funcref)
elem(0, :dead, :dead, :dead, :alive, :dead, :dead, :dead, :dead,
:dead, :dead, :dead, :alive, :alive, :dead, :dead, :dead,
:dead, :dead)
func(:alive) do
result(:i32)
i32.const(1)
end
func(:dead) do
result(:i32)
i32.const(0)
end
func(:offsetFromCoordinate) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.add do
i32.mul do
i32.const(200)
local.get(:y)
end
i32.mul do
i32.const(4)
local.get(:x)
end
end
end
func(:setCell) do
param(:x, :i32)
param(:y, :i32)
param(:value, :i32)
i32.store do
call(:offsetFromCoordinate) do
local.get(:x)
local.get(:y)
end
local.get(:value)
end
end
func(:getCell) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
if_ do
result(:i32)
block do
result(:i32)
i32.and do
call(:inRange) do
i32.const(0)
i32.const(50)
local.get(:x)
end
call(:inRange) do
i32.const(0)
i32.const(50)
local.get(:y)
end
end
end
then_ do
i32.load8_u do
call(:offsetFromCoordinate) do
local.get(:x)
local.get(:y)
end
end
end
else_ do
i32.const(0)
end
end
end
func(:liveNeighbourCount) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.const(0)
# add the cell value from x + 1, y
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
local.get(:y)
end
i32.add
# add the cell value from x - 1, y
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
local.get(:y)
end
i32.add
# add the cell value from x, y - 1
call(:isCellAlive) do
local.get(:x)
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x - 1, y - 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x + 1, y - 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x, y + 1
call(:isCellAlive) do
local.get(:x)
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
# add the cell value from x - 1, y + 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
# add the cell value from x + 1, y + 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
end
func(:inRange) do
param(:low, :i32)
param(:high, :i32)
param(:value, :i32)
result(:i32)
i32.and do
i32.ge_s do
local.get(:value)
local.get(:low)
end
i32.lt_s do
local.get(:value)
local.get(:high)
end
end
end
func(:isCellAlive) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.and do
call(:getCell) do
local.get(:x)
local.get(:y)
end
i32.const(1)
end
end
func(:setCellStateForNextGeneration) do
param(:x, :i32)
param(:y, :i32)
param(:value, :i32)
call(:setCell) do
local.get(:x)
local.get(:y)
i32.or do
call(:isCellAlive) do
local.get(:x)
local.get(:y)
end
i32.shl do
local.get(:value)
i32.const(1)
end
end
end
end
func(:evolveCellToNextGeneration) do
param(:x, :i32)
param(:y, :i32)
call(:setCellStateForNextGeneration) do
local.get(:x)
local.get(:y)
call_indirect do
result(:i32)
i32.or do
i32.mul do
i32.const(9)
call(:isCellAlive) do
local.get(:x)
local.get(:y)
end
end
call(:liveNeighbourCount) do
local.get(:x)
local.get(:y)
end
end
end
end
end
func(:increment) do
param(:value, :i32)
result(:i32)
i32.add do
local.get(:value)
i32.const(1)
end
end
func(:evolveAllCells) do
local(:x, :i32)
local(:y, :i32)
local.set(:x) do
i32.const(0)
end
local.set(:y) do
i32.const(0)
end
block do
loop_ do
local.set(:x) do
i32.const(0)
end
block do
loop_ do
call(:evolveCellToNextGeneration) do
local.get(:x)
local.get(:y)
end
local.set(:x) do
call(:increment) do
local.get(:x)
end
end
br_if(1) do
i32.eq do
local.get(:x)
i32.const(50)
end
end
br(0)
end
end
local.set(:y) do
call(:increment) do
local.get(:y)
end
end
br_if(1) do
i32.eq do
local.get(:y)
i32.const(50)
end
end
br(0)
end
end
end
func(:promoteNextGeneration) do
local(:x, :i32)
local(:y, :i32)
local.set(:x) do
i32.const(0)
end
local.set(:y) do
i32.const(0)
end
block do
loop_ do
local.set(:x) do
i32.const(0)
end
block do
loop_ do
call(:setCell) do
local.get(:x)
local.get(:y)
i32.shr_u do
call(:getCell) do
local.get(:x)
local.get(:y)
end
i32.const(1)
end
end
local.set(:x) do
call(:increment) do
local.get(:x)
end
end
br_if(1) do
i32.eq do
local.get(:x)
i32.const(50)
end
end
br(0)
end
end
local.set(:y) do
call(:increment) do
local.get(:y)
end
end
br_if(1) do
i32.eq do
local.get(:y)
i32.const(50)
end
end
br(0)
end
end
end
func(:tick) do
call(:evolveAllCells)
call(:promoteNextGeneration)
end
export('tick') do
func(:tick)
end
export('promoteNextGeneration') do
func(:promoteNextGeneration)
end
export('evolveAllCells') do
func(:evolveAllCells)
end
export('evolveCellToNextGeneration') do
func(:evolveCellToNextGeneration)
end
export('setCellStateForNextGeneration') do
func(:setCellStateForNextGeneration)
end
export('isCellAlive') do
func(:isCellAlive)
end
export('inRange') do
func(:inRange)
end
export('offsetFromCoordinate') do
func(:offsetFromCoordinate)
end
export('liveNeighbourCount') do
func(:liveNeighbourCount)
end
export('getCell') do
func(:getCell)
end
export('setCell') do
func(:setCell)
end
export('memory') do
memory(:mem)
end
end
end
describe '#to_wasm' do
subject { mod.to_wasm }
it { is_expected.to be_valid }
end
end
end