WIP
This commit is contained in:
parent
307ea895c2
commit
f5173e8f9e
54 changed files with 1038 additions and 8 deletions
29
.rubocop.yml
Normal file
29
.rubocop.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
||||||
|
# configuration file. It makes it possible to enable/disable
|
||||||
|
# certain cops (checks) and to alter their behavior if they accept
|
||||||
|
# any parameters. The file can be placed either in your home
|
||||||
|
# directory or in some project directory.
|
||||||
|
#
|
||||||
|
# RuboCop will start looking for the configuration file in the directory
|
||||||
|
# where the inspected file is and continue its way up to the root directory.
|
||||||
|
#
|
||||||
|
# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md
|
||||||
|
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 120
|
||||||
|
|
||||||
|
Style/HashEachMethods:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/HashTransformKeys:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/HashTransformValues:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Metrics/BlockLength:
|
||||||
|
Exclude:
|
||||||
|
- spec/**/*_spec.rb
|
||||||
|
|
||||||
|
Style/Documentation:
|
||||||
|
Enabled: false
|
1
Gemfile
1
Gemfile
|
@ -5,6 +5,7 @@ source 'https://rubygems.org'
|
||||||
# Specify your gem's dependencies in wag.gemspec
|
# Specify your gem's dependencies in wag.gemspec
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
|
gem 'pry', '~> 0.13.0'
|
||||||
gem 'rake', '~> 12.0'
|
gem 'rake', '~> 12.0'
|
||||||
gem 'rspec', '~> 3.0'
|
gem 'rspec', '~> 3.0'
|
||||||
gem 'rubocop', '~> 0.80.1'
|
gem 'rubocop', '~> 0.80.1'
|
||||||
|
|
|
@ -2,16 +2,23 @@ PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
wag (0.1.0)
|
wag (0.1.0)
|
||||||
|
dry-inflector (~> 0.2.0)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
|
coderay (1.1.2)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
|
dry-inflector (0.2.0)
|
||||||
jaro_winkler (1.5.4)
|
jaro_winkler (1.5.4)
|
||||||
|
method_source (1.0.0)
|
||||||
parallel (1.19.1)
|
parallel (1.19.1)
|
||||||
parser (2.7.0.5)
|
parser (2.7.0.5)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
|
pry (0.13.0)
|
||||||
|
coderay (~> 1.1)
|
||||||
|
method_source (~> 1.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (12.3.3)
|
rake (12.3.3)
|
||||||
rexml (3.2.4)
|
rexml (3.2.4)
|
||||||
|
@ -43,6 +50,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
pry (~> 0.13.0)
|
||||||
rake (~> 12.0)
|
rake (~> 12.0)
|
||||||
rspec (~> 3.0)
|
rspec (~> 3.0)
|
||||||
rubocop (~> 0.80.1)
|
rubocop (~> 0.80.1)
|
||||||
|
|
22
README.md
22
README.md
|
@ -1,8 +1,24 @@
|
||||||
# Wag
|
# WAG - The WebAssembly Code Generator
|
||||||
|
|
||||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/wag`. To experiment with that code, run `bin/console` for an interactive prompt.
|
This Ruby gem allows you to generate WebAssembly programs programatically.
|
||||||
|
|
||||||
TODO: Delete this and the text above, and describe your gem
|
## Example
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
WAG::Module.new.build do
|
||||||
|
memory(1)
|
||||||
|
export('memory', :memory)
|
||||||
|
export('hello', :$hello)
|
||||||
|
func(:$hello) do
|
||||||
|
result(:i32)
|
||||||
|
i32.const(16)
|
||||||
|
end
|
||||||
|
data do
|
||||||
|
i32.const(16)
|
||||||
|
<< "Hello World\00"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
20
lib/wag.rb
20
lib/wag.rb
|
@ -2,7 +2,25 @@
|
||||||
|
|
||||||
require 'wag/version'
|
require 'wag/version'
|
||||||
|
|
||||||
module Wag
|
module WAG
|
||||||
class Error < StandardError; end
|
class Error < StandardError; end
|
||||||
# Your code goes here...
|
# Your code goes here...
|
||||||
|
|
||||||
|
require 'wag/wat'
|
||||||
|
|
||||||
|
require 'wag/data'
|
||||||
|
require 'wag/function'
|
||||||
|
require 'wag/function_type'
|
||||||
|
require 'wag/global'
|
||||||
|
require 'wag/element'
|
||||||
|
require 'wag/export'
|
||||||
|
require 'wag/import'
|
||||||
|
require 'wag/indices'
|
||||||
|
require 'wag/inflector'
|
||||||
|
require 'wag/instruction'
|
||||||
|
require 'wag/label'
|
||||||
|
require 'wag/memory'
|
||||||
|
require 'wag/module'
|
||||||
|
require 'wag/table'
|
||||||
|
require 'wag/type'
|
||||||
end
|
end
|
||||||
|
|
18
lib/wag/data.rb
Normal file
18
lib/wag/data.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Data
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :offset, :value
|
||||||
|
|
||||||
|
def initialize(offset, value)
|
||||||
|
@offset = offset.to_i
|
||||||
|
@value = value
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:data, [:"i32.const", offset], value]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
18
lib/wag/element.rb
Normal file
18
lib/wag/element.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Element
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :table_id, :labels
|
||||||
|
|
||||||
|
def initialize(table_id, *labels)
|
||||||
|
@table_id = table_id.to_i
|
||||||
|
@labels = labels.map { |l| WAG::Label.from(l) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:elem, [:"i32.const", table_id]].concat(labels.map(&:to_sexpr))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
39
lib/wag/export.rb
Normal file
39
lib/wag/export.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Export
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :name, :desc
|
||||||
|
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
def func(label, &block)
|
||||||
|
@desc = Function.new(label)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def global(label, type)
|
||||||
|
@desc = Global.new(label, type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def memory(number, min: nil, max: nil, &block)
|
||||||
|
@desc = Memory.new(number, min: min, max: max)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def table(number, type = :funcref, min: nil, max: nil, &block)
|
||||||
|
@desc = Table.new(number, type, min: min, max: max)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:export, name, desc.to_sexpr]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
30
lib/wag/function.rb
Normal file
30
lib/wag/function.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Function
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :label, :params, :results
|
||||||
|
|
||||||
|
def initialize(label)
|
||||||
|
@label = WAG::Label.from(label)
|
||||||
|
@params = []
|
||||||
|
@results = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def param(*types)
|
||||||
|
@params.concat(types.map { |t| WAG::Type.from(t) })
|
||||||
|
end
|
||||||
|
|
||||||
|
def result(*types)
|
||||||
|
@results.concat(types.map { |t| WAG::Type.from(t) })
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:func, @label.to_sexpr].tap do |func|
|
||||||
|
func.push([:param].concat(@params.map(&:to_sexpr))) if @params
|
||||||
|
func.push([:result].concat(@results.map(&:to_sexpr))) if @results
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
31
lib/wag/function_type.rb
Normal file
31
lib/wag/function_type.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class FunctionType
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :label, :params, :results
|
||||||
|
|
||||||
|
def initialize(label)
|
||||||
|
@label = WAG::Label.from(label)
|
||||||
|
@params = []
|
||||||
|
@results = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def param(*types)
|
||||||
|
@params.concat(types.map { |t| WAG::Type.from(t) })
|
||||||
|
end
|
||||||
|
|
||||||
|
def result(*types)
|
||||||
|
@results.concat(types.map { |t| WAG::Type.from(t) })
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:type, @label.to_sexpr,
|
||||||
|
[:func].tap do |func|
|
||||||
|
func.push([:param].concat(@params.map(&:to_sexpr))) if @params
|
||||||
|
func.push([:result].concat(@results.map(&:to_sexpr))) if @results
|
||||||
|
end]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
18
lib/wag/global.rb
Normal file
18
lib/wag/global.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Global
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :label, :type
|
||||||
|
|
||||||
|
def initialize(label, type)
|
||||||
|
@label = WAG::Label.from(label)
|
||||||
|
@type = WAG::Type.from(type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:global, label.to_sexpr, type.to_sexpr]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
42
lib/wag/import.rb
Normal file
42
lib/wag/import.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Import
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :module_name, :name, :desc
|
||||||
|
|
||||||
|
def initialize(module_name, name)
|
||||||
|
@module_name = module_name
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
def func(label, &block)
|
||||||
|
@desc = Function.new(label)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def global(label, type, &block)
|
||||||
|
@desc = Global.new(label, type)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def memory(number, min: nil, max: nil, &block)
|
||||||
|
@desc = Memory.new(number, min: min, max: max)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def table(number, type = :funcref, min: nil, max: nil, &block)
|
||||||
|
@desc = Table.new(number, type, min: min, max: max)
|
||||||
|
@desc.instance_exec(&block) if block
|
||||||
|
@desc
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[:import, module_name, name, desc.to_sexpr]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
lib/wag/indices.rb
Normal file
14
lib/wag/indices.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
require 'wag/indices/base'
|
||||||
|
require 'wag/indices/func'
|
||||||
|
require 'wag/indices/global'
|
||||||
|
require 'wag/indices/label'
|
||||||
|
require 'wag/indices/local'
|
||||||
|
require 'wag/indices/mem'
|
||||||
|
require 'wag/indices/table'
|
||||||
|
require 'wag/indices/type'
|
||||||
|
end
|
||||||
|
end
|
16
lib/wag/indices/base.rb
Normal file
16
lib/wag/indices/base.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Base
|
||||||
|
RANGE = [0..0xFFFFFFFF].freeze
|
||||||
|
attr_reader :value
|
||||||
|
|
||||||
|
def initialize(value)
|
||||||
|
raise "Index 0x#{value.to_s(16)} does not fit in an i32" unless RANGE.cover?(value)
|
||||||
|
|
||||||
|
@value = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/func.rb
Normal file
8
lib/wag/indices/func.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Func < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/global.rb
Normal file
8
lib/wag/indices/global.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Global < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/label.rb
Normal file
8
lib/wag/indices/label.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Label < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/local.rb
Normal file
8
lib/wag/indices/local.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Local < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/mem.rb
Normal file
8
lib/wag/indices/mem.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Mem < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/table.rb
Normal file
8
lib/wag/indices/table.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Table < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/indices/type.rb
Normal file
8
lib/wag/indices/type.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Indices
|
||||||
|
class Type < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
lib/wag/inflector.rb
Normal file
13
lib/wag/inflector.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'dry-inflector'
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Inflector
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def inflector
|
||||||
|
@inflector ||= Dry::Inflector.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
lib/wag/instruction.rb
Normal file
19
lib/wag/instruction.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
require 'wag/instructions/base'
|
||||||
|
|
||||||
|
require 'wag/instructions/block'
|
||||||
|
require 'wag/instructions/br_if'
|
||||||
|
require 'wag/instructions/br_table'
|
||||||
|
require 'wag/instructions/br'
|
||||||
|
require 'wag/instructions/else'
|
||||||
|
require 'wag/instructions/end'
|
||||||
|
require 'wag/instructions/if'
|
||||||
|
require 'wag/instructions/loop'
|
||||||
|
require 'wag/instructions/nop'
|
||||||
|
require 'wag/instructions/return'
|
||||||
|
require 'wag/instructions/unreachable'
|
||||||
|
end
|
||||||
|
end
|
24
lib/wag/instructions/base.rb
Normal file
24
lib/wag/instructions/base.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Base
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
def self.instruction(op_code)
|
||||||
|
klass = Class.new(WAG::Instruction::Base)
|
||||||
|
klass.define_method(:op_code) { op_code }
|
||||||
|
klass
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
name = WAG::Inflector.inflector.demodulize(self.class.name.to_s)
|
||||||
|
WAG::Inflector.inflector.underscore(name).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
name.to_sym
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/block.rb
Normal file
8
lib/wag/instructions/block.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Block < Base.instruction(0x02)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
lib/wag/instructions/br.rb
Normal file
17
lib/wag/instructions/br.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Br < Base.instruction(0x0c)
|
||||||
|
attr_reader :label
|
||||||
|
|
||||||
|
def initialize(label)
|
||||||
|
@label = WAG::Label.from(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[name, label.to_sexpr]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
lib/wag/instructions/br_if.rb
Normal file
17
lib/wag/instructions/br_if.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class BrIf < Base.instruction(0x0d)
|
||||||
|
attr_reader :label
|
||||||
|
|
||||||
|
def initialize(label)
|
||||||
|
@label = WAG::Label.from(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[name, label.to_sexpr]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
lib/wag/instructions/br_table.rb
Normal file
17
lib/wag/instructions/br_table.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class BrTable < Base.instruction(0x0e)
|
||||||
|
attr_reader :label
|
||||||
|
|
||||||
|
def initialize(*labels)
|
||||||
|
@labels = labels.map { |label| WAG::Label.from(label) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
[name].concat(@labels.map(&:to_sexpr))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/else.rb
Normal file
8
lib/wag/instructions/else.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Else < Base.instruction(0x05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/end.rb
Normal file
8
lib/wag/instructions/end.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class End < Base.instruction(0x0b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/if.rb
Normal file
8
lib/wag/instructions/if.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class If < Base.instruction(0x04)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/loop.rb
Normal file
8
lib/wag/instructions/loop.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Loop < Base.instruction(0x03)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/nop.rb
Normal file
8
lib/wag/instructions/nop.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Nop < Base.instruction(0x01)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/return.rb
Normal file
8
lib/wag/instructions/return.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Return < Base.instruction(0x0f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/instructions/unreachable.rb
Normal file
8
lib/wag/instructions/unreachable.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Instruction
|
||||||
|
class Unreachable < Base.instruction(0x00)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
24
lib/wag/label.rb
Normal file
24
lib/wag/label.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Label
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :value
|
||||||
|
|
||||||
|
def initialize(value)
|
||||||
|
@value = value.to_s
|
||||||
|
@value = @value[1..-1] if @value.start_with?('$')
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
"$#{value}".to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from(value)
|
||||||
|
return value if value.is_a?(WAG::Label)
|
||||||
|
|
||||||
|
WAG::Label.new(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
24
lib/wag/memory.rb
Normal file
24
lib/wag/memory.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Memory
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
attr_reader :number, :min, :max
|
||||||
|
|
||||||
|
def initialize(number, min: nil, max: nil)
|
||||||
|
@number = number
|
||||||
|
@min = min
|
||||||
|
@max = max
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
wat = [:memory, number]
|
||||||
|
|
||||||
|
wat.concat([:min, min]) if min
|
||||||
|
wat.concat([:max, max]) if max
|
||||||
|
|
||||||
|
wat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
87
lib/wag/module.rb
Normal file
87
lib/wag/module.rb
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Module
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@data = []
|
||||||
|
@elements = []
|
||||||
|
@exports = []
|
||||||
|
@functions = []
|
||||||
|
@globals = []
|
||||||
|
@imports = []
|
||||||
|
@memories = []
|
||||||
|
@types = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def import(module_name, name, &block)
|
||||||
|
import = Import.new(module_name, name)
|
||||||
|
@imports << import
|
||||||
|
import.instance_exec(&block) if block
|
||||||
|
import
|
||||||
|
end
|
||||||
|
|
||||||
|
def export(name, &block)
|
||||||
|
export = Export.new(name)
|
||||||
|
@exports << export
|
||||||
|
export.instance_exec(&block) if block
|
||||||
|
export
|
||||||
|
end
|
||||||
|
|
||||||
|
def memory(number, min: nil, max: nil, &block)
|
||||||
|
mem = Memory.new(number, min: min, max: max)
|
||||||
|
@memories << mem
|
||||||
|
mem.instance_exec(&block) if block
|
||||||
|
mem
|
||||||
|
end
|
||||||
|
|
||||||
|
def global(label, type, &block)
|
||||||
|
global = Global.new(label, type)
|
||||||
|
@globals << global
|
||||||
|
global.instance_exec(&block) if block
|
||||||
|
global
|
||||||
|
end
|
||||||
|
|
||||||
|
def type(label, &block)
|
||||||
|
type = FunctionType.new(label)
|
||||||
|
@types << type
|
||||||
|
type.instance_exec(&block) if block
|
||||||
|
type
|
||||||
|
end
|
||||||
|
|
||||||
|
def elem(table_id, *labels, &block)
|
||||||
|
elem = Element.new(table_id, *labels)
|
||||||
|
@elements << elem
|
||||||
|
elem.instance_exec(&block) if block
|
||||||
|
elem
|
||||||
|
end
|
||||||
|
|
||||||
|
def data(offset, value, &block)
|
||||||
|
data = Data.new(offset, value)
|
||||||
|
@data << data
|
||||||
|
data.instance_exec(&block) if block
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def func(label, &block)
|
||||||
|
func = Function.new(label)
|
||||||
|
@functions << func
|
||||||
|
func.instance_exec(&block) if block
|
||||||
|
func
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr # rubocop:disable Metrics/AbcSize
|
||||||
|
mod = [:module]
|
||||||
|
mod.concat(@imports.map(&:to_sexpr))
|
||||||
|
mod.concat(@exports.map(&:to_sexpr))
|
||||||
|
mod.concat(@memories.map(&:to_sexpr))
|
||||||
|
mod.concat(@globals.map(&:to_sexpr))
|
||||||
|
mod.concat(@types.map(&:to_sexpr))
|
||||||
|
mod.concat(@elements.map(&:to_sexpr))
|
||||||
|
mod.concat(@data.map(&:to_sexpr))
|
||||||
|
mod.concat(@functions.map(&:to_sexpr))
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
lib/wag/table.rb
Normal file
29
lib/wag/table.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
class Table
|
||||||
|
include WAG::WAT
|
||||||
|
|
||||||
|
RANGE = (0..0xFFFFFFFF).freeze
|
||||||
|
|
||||||
|
attr_reader :number, :type, :min, :max
|
||||||
|
|
||||||
|
def initialize(number, type = :funcref, min: nil, max: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||||
|
raise '`min` must fit in u32 range' if min && !RANGE.cover?(min)
|
||||||
|
raise '`max` must fit in u32 range' if max && !RANGE.cover?(max)
|
||||||
|
raise '`max` must be greater than `min`' if min && max && min > max
|
||||||
|
|
||||||
|
@number = number
|
||||||
|
@type = type
|
||||||
|
@min = min
|
||||||
|
@max = max
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_sexpr
|
||||||
|
expr = [:table, number, type]
|
||||||
|
expr.concat([:min, min]) if min
|
||||||
|
expr.concat([:max, max]) if max
|
||||||
|
expr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
lib/wag/type.rb
Normal file
19
lib/wag/type.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
require 'wag/types/base'
|
||||||
|
require 'wag/types/i32'
|
||||||
|
require 'wag/types/i64'
|
||||||
|
require 'wag/types/f32'
|
||||||
|
require 'wag/types/f64'
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def from(type)
|
||||||
|
return type if type.is_a?(WAG::Type::Base)
|
||||||
|
|
||||||
|
const_get(WAG::Inflector.inflector.camelize(type.to_s)).new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
lib/wag/types/base.rb
Normal file
16
lib/wag/types/base.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
class Base
|
||||||
|
def to_sexpr
|
||||||
|
name = WAG::Inflector.inflector.demodulize(self.class.name.to_s)
|
||||||
|
WAG::Inflector.inflector.underscore(name).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def value?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/types/f32.rb
Normal file
8
lib/wag/types/f32.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
class F32 < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/types/f64.rb
Normal file
8
lib/wag/types/f64.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
class F64 < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
lib/wag/types/function.rb
Normal file
28
lib/wag/types/function.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
# A WebAssembly function type.
|
||||||
|
class Function < Base
|
||||||
|
attr_reader :parameter_types, :return_types
|
||||||
|
|
||||||
|
def initialize(parameter_types, return_types)
|
||||||
|
parameter_types.map! { |t| Huia::Type.from(t) }
|
||||||
|
return_types.map! { |t| Huia::Type.from(t) }
|
||||||
|
|
||||||
|
raise 'There must only be one return type (at the moment)' unless return_types.one?
|
||||||
|
|
||||||
|
unless parameter_types.all?(&:value?) && return_types.all?(&:value?)
|
||||||
|
raise 'Function type parameters can only be value types'
|
||||||
|
end
|
||||||
|
|
||||||
|
@parameter_types = parameter_types
|
||||||
|
@return_types = return_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def value?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/types/i32.rb
Normal file
8
lib/wag/types/i32.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
class I32 < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
lib/wag/types/i64.rb
Normal file
8
lib/wag/types/i64.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
module Type
|
||||||
|
class I64 < Base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Wag
|
module WAG
|
||||||
VERSION = '0.1.0'
|
VERSION = '0.1.0'
|
||||||
end
|
end
|
||||||
|
|
19
lib/wag/wat.rb
Normal file
19
lib/wag/wat.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WAG
|
||||||
|
# A simple mixin which formats s-expressions as WAT.
|
||||||
|
module WAT
|
||||||
|
def to_wat
|
||||||
|
wat_encode(to_sexpr)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def wat_encode(data)
|
||||||
|
return "(#{data.map { |e| wat_encode(e) }.join(' ')})" if data.is_a?(Enumerable)
|
||||||
|
return data.inspect if data.is_a?(String)
|
||||||
|
|
||||||
|
data.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
require 'bundler/setup'
|
require 'bundler/setup'
|
||||||
require 'wag'
|
require 'wag'
|
||||||
|
require 'pry'
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# Enable flags like --only-failures and --next-failure
|
# Enable flags like --only-failures and --next-failure
|
||||||
|
|
55
spec/wag/export_spec.rb
Normal file
55
spec/wag/export_spec.rb
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe WAG::Export do
|
||||||
|
let(:export) { described_class.new('marty') }
|
||||||
|
subject { export }
|
||||||
|
|
||||||
|
describe 'When exporting memory' do
|
||||||
|
before do
|
||||||
|
export.memory(1, min: 1, max: 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:export, 'marty', [:memory, 1, :min, 1, :max, 64]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When exporting a table' do
|
||||||
|
before do
|
||||||
|
export.table(1, :funcref, min: 1, max: 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:export, 'marty', [:table, 1, :funcref, :min, 1, :max, 64]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When exporting a function' do
|
||||||
|
before do
|
||||||
|
export.func(:foo) do
|
||||||
|
param(:f32, :f64)
|
||||||
|
result(:i32)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:export, 'marty', [:func, :$foo, %i[param f32 f64], %i[result i32]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When exporting a global' do
|
||||||
|
before do
|
||||||
|
export.global(:foo, :i32)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:export, 'marty', %i[global $foo i32]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
55
spec/wag/import_spec.rb
Normal file
55
spec/wag/import_spec.rb
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe WAG::Import do
|
||||||
|
let(:import) { described_class.new('marty', 'mcfly') }
|
||||||
|
subject { import }
|
||||||
|
|
||||||
|
describe 'When importing memory' do
|
||||||
|
before do
|
||||||
|
import.memory(1, min: 1, max: 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:import, 'marty', 'mcfly', [:memory, 1, :min, 1, :max, 64]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When importing a table' do
|
||||||
|
before do
|
||||||
|
import.table(1, :funcref, min: 1, max: 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:import, 'marty', 'mcfly', [:table, 1, :funcref, :min, 1, :max, 64]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When importing a function' do
|
||||||
|
before do
|
||||||
|
import.func(:foo) do
|
||||||
|
param(:f32, :f64)
|
||||||
|
result(:i32)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:import, 'marty', 'mcfly', [:func, :$foo, %i[param f32 f64], %i[result i32]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When importing a global' do
|
||||||
|
before do
|
||||||
|
import.global(:foo, :i32)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:import, 'marty', 'mcfly', %i[global $foo i32]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
114
spec/wag/module_spec.rb
Normal file
114
spec/wag/module_spec.rb
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe WAG::Module do
|
||||||
|
let(:mod) { described_class.new }
|
||||||
|
subject { mod }
|
||||||
|
|
||||||
|
describe 'When the module is empty' do
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module] }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_wat' do
|
||||||
|
subject { super().to_wat }
|
||||||
|
it { is_expected.to eq '(module)' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module imports a memory' do
|
||||||
|
before do
|
||||||
|
mod.import('js', 'memory').memory(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:import, 'js', 'memory', [:memory, 1]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module exports a memory' do
|
||||||
|
before do
|
||||||
|
mod.export('memory').memory(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:export, 'memory', [:memory, 1]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares a memory' do
|
||||||
|
before do
|
||||||
|
mod.memory(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:memory, 2]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares a global' do
|
||||||
|
before do
|
||||||
|
mod.global(:foo, :i32)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, %i[global $foo i32]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares a type' do
|
||||||
|
before do
|
||||||
|
mod.type(:foo) do
|
||||||
|
param(:i32)
|
||||||
|
result(:i32)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:type, :$foo, [:func, %i[param i32], %i[result i32]]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares a table element' do
|
||||||
|
before do
|
||||||
|
mod.elem(1, :foo1, :foo2)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:elem, [:"i32.const", 1], :$foo1, :$foo2]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares data' do
|
||||||
|
before do
|
||||||
|
mod.data(16, 'Hello World')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:data, [:"i32.const", 16], 'Hello World']] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'When the module declares a function' do
|
||||||
|
before do
|
||||||
|
mod.func(:add) do
|
||||||
|
param(:i32, :i32)
|
||||||
|
result(:i32)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_sexpr' do
|
||||||
|
subject { super().to_sexpr }
|
||||||
|
it { is_expected.to eq [:module, [:func, :$add, %i[param i32 i32], %i[result i32]]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Wag do
|
RSpec.describe WAG do
|
||||||
it 'has a version number' do
|
it 'has a version number' do
|
||||||
expect(Wag::VERSION).not_to be nil
|
expect(WAG::VERSION).not_to be nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ require_relative 'lib/wag/version'
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = 'wag'
|
spec.name = 'wag'
|
||||||
spec.version = Wag::VERSION
|
spec.version = WAG::VERSION
|
||||||
spec.authors = ['James Harton']
|
spec.authors = ['James Harton']
|
||||||
spec.email = ['james@automat.nz']
|
spec.email = ['james@automat.nz']
|
||||||
|
|
||||||
|
@ -30,4 +30,6 @@ Gem::Specification.new do |spec|
|
||||||
spec.bindir = 'exe'
|
spec.bindir = 'exe'
|
||||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||||
spec.require_paths = ['lib']
|
spec.require_paths = ['lib']
|
||||||
|
|
||||||
|
spec.add_runtime_dependency('dry-inflector', '~> 0.2.0')
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue