1. Function Definition and Export

(module
  (func
    (export "main")  ;; Name to export
    (result i32)     ;; Return Type
    i32.const 42     ;; Return value
  )
)

The main function can now be called from the host environment, for example: Python running Wasmer.

2. Module Local Function Definition

  ;; Signature: average(x: f32, y: f32) -> f32
  (func $average     ;; Name inside module
    (param $x f32)   ;; Parameter names and types
    (param $y f32)
    (result f32)     ;; Return type

    local.get $x
    local.get $y
    f32.add
    f32.const 2.0
    f32.div          ;; Return (x + y) / 2.0
  )

The following code calls the previous function:

    ;; average(7.25, 3.75)
    f32.const 7.25  ;; Push arguments on top of stack
    f32.const 3.75
    call $average   ;; Call and place result on top of stack

3. Parameters and Local Variables

  ;; Signature: sum(x: i32, y: i32, z: i32) -> i32
  (func $sum
    (param $x i32)  ;; Parameter names and types
    (param $y i32)
    (param $z i32)
    (result i32)    ;; Return type

    (local $a i32)  ;; Local variable names and types
    (local $b i32)

    local.get $x
    local.get $y
    i32.add
    local.set $a    ;; a = x + y

    local.get $z
    local.get $a
    i32.add
    local.set $b    ;; b = z + a

    local.get $b    ;; Return b
  )

4. Function Import

(module
  ;; Import from module "example" the function called "myfun"
  (import "example" "myfun"
    (func $myfun     ;; Name inside module
      (param i32)    ;; Parameter types
      (param i32)
      (result i32))) ;; Return type
  ;; ...
)

Imports from the hosting environment the named function. The function can now be called like a module local function.

5. Global Variables

(module
  ;; $t as a mutable global variable of type i32
  ;; with initial value 0
  (global $t (mut i32) (i32.const 0))

  (func $inc_t
    global.get $t
    i32.const 1
    i32.add
    global.set $t  ;; t = t + 1
  )
  ;; ...
)

6. Array Literals

    (local $_temp i32)
    (local $a i32)
    ;; ...

    ;; a = [1 + 1, 2 * 3, 13];
    i32.const 0
    call $new
    local.set $_temp
    local.get $_temp
    local.get $_temp
    local.get $_temp
    local.get $_temp

    i32.const 1
    i32.const 1
    i32.add      ;; 1 + 1
    call $add
    drop

    i32.const 2
    i32.const 3
    i32.mul      ;; 2 * 3
    call $add
    drop

    i32.const 13 ;; 13
    call $add
    drop

    local.set $a

7. String Literals

    (local $_temp i32)
    (local $s i32)
    ;; ...

    ;; s = "ABC\n";
    i32.const 0
    call $new
    local.set $_temp
    local.get $_temp
    local.get $_temp
    local.get $_temp
    local.get $_temp
    local.get $_temp

    i32.const 65 ;; 'A'
    call $add
    drop

    i32.const 66 ;; 'B'
    call $add
    drop

    i32.const 67 ;; 'C'
    call $add
    drop

    i32.const 10 ;; '\n'
    call $add
    drop

    local.set $s

The following C# program demonstrates how to get a list with all the code points from an arbitrary Unicode string.

using System;
using System.Collections.Generic;

class CodePoints {
    public static IList<int> AsCodePoints(String str) {
        var result = new List<int>(str.Length);
        for (var i = 0; i < str.Length; i++) {
            result.Add(char.ConvertToUtf32(str, i));
            if (char.IsHighSurrogate(str, i)) {
                i++;
            }
        }
        return result;
    }

    public static void Main() {
        var codes = AsCodePoints("¡Ñoño!");
        foreach (var code in codes) {
            Console.WriteLine(code);
        }
    }
}

8. If Statement

    (local $x i32)
    ;; ...

    ;; if (1 < 2) {
    ;;     x = 3;
    ;; }

    i32.const 1
    i32.const 2
    i32.lt_s       ;; 1 < 2
    if
      i32.const 3
      local.set $x ;; x = 3;
    end

9. If/Else Statement

    (local $x i32)
    ;; ...

    ;; if (1 < 2) {
    ;;     x = 3;
    ;; } else {
    ;;     x = 4;
    ;; }

    i32.const 1
    i32.const 2
    i32.lt_s       ;; 1 < 2
    if
      i32.const 3
      local.set $x ;; x = 3;
    else
      i32.const 4
      local.set $x ;; x = 4;
    end

10. If/Elif/Else Statement

    (local $x i32)
    ;; ...

    ;; if (1 < 2) {
    ;;     x = 3;
    ;; } elif (4 < 5) {
    ;;     x = 6;
    ;; } else {
    ;;     x = 7;
    ;; }

    i32.const 1
    i32.const 2
    i32.lt_s         ;; 1 < 2
    if
      i32.const 3
      local.set $x   ;; x = 3;
    else
      i32.const 4
      i32.const 5
      i32.lt_s       ;; 4 < 5
      if
        i32.const 6
        local.set $x ;; x = 6;
      else
        i32.const 7
        local.set $x ;; x = 7;
      end
    end

11. Neg Operator

    (local $x i32)
    ;; ...

    ;; x = - 5;

    i32.const 0  ;; Always push 0
    i32.const 5  ;; Compute operand
    i32.sub      ;; Get negated value

    local.set $x ;; x = - 5 

12. Not Operator

    (local $x i32)
    ;; ...

    ;; x = not 2;

    i32.const 2  ;; Compute operand
    i32.eqz      ;; Pop top of stack and compare to 0. If it's equal
                 ;; push true (1), if not push false (0).

    local.set $x ;; x = not 2; 

13. Short-circuit And Operator

    (local $x i32)
    ;; ...

    ;; x = 2 and 3;

    i32.const 2     ;; Compute first operand
    if (result i32) ;; Result of IF placed on top of stack
      i32.const 3   ;; Compute second operand
      i32.eqz       ;; Two consecutive EQZ instructions convert
      i32.eqz       ;;   top of stack to true (1) or false (0)
    else
      i32.const 0   ;; false
    end

    local.set $x    ;; x = 2 and 3;

14. Short-circuit Or Operator

    (local $x i32)
    ;; ...

    ;; x = 2 or 3;

    i32.const 2     ;; Compute first operand
    if (result i32) ;; Result of IF placed on top of stack
      i32.const 1   ;; true
    else
      i32.const 3   ;; Compute second operand
      i32.eqz       ;; Two consecutive EQZ instructions convert
      i32.eqz       ;;   top of stack to true (1) or false (0)
    end

    local.set $x    ;; x = 2 or 3;

15. Loop Statements

    ;; n = 5;
    ;; r = 1;
    ;; i = 1;
    ;; while (i <= n) {
    ;;     r = r * i;
    ;;     i = i + 1;
    ;; }

    (local $n i32)
    (local $r i32)
    (local $i i32)

    i32.const 5
    local.set $n      ;; n = 5;

    i32.const 1
    local.set $r      ;; r = 1;

    i32.const 1
    local.set $i      ;; i = 1;

    block $00001      ;; Target for BREAK
      loop $00002     ;; Target for CONTINUE

        local.get $i
        local.get $n
        i32.le_s      ;; i <= n

        i32.eqz
        br_if $00001  ;; BREAK when i <= n is false

        local.get $r
        local.get $i
        i32.mul
        local.set $r  ;; r = r * i;

        local.get $i
        i32.const 1
        i32.add
        local.set $i  ;; i = i + 1;

        br $00002     ;; CONTINUE always
      end             ;; End of loop
    end               ;; End of block

The following C# code shows a method that generates unique labels that can be used in loops:

class WATVisitor {

    int labelCounter = 0;

    public String GenerateLabel() {
        return String.Format("${0:00000}", labelCounter++);
    }

    // Rest of the class goes here

}

16. Break Statement

There might be nested loops. Thus, you need to keep track of the most recent (nested) label for the current block instruction, which is the target for the break instruction. A dedicated stack can be used for this purpose.

    ;; i = 1;
    ;; while (true) {
    ;;     i = i + 3;
    ;;     if (i % 5 == 0) {
    ;;         break;
    ;;     }
    ;; }

    (local $i i32)

    i32.const 1
    local.set $i     ;; i = 1;

    block $00001     ;; Target for BREAK
      loop $00002    ;; Target for CONTINUE

        i32.const 1  ;; true

        i32.eqz
        br_if $00001 ;; BREAK when true is false

        local.get $i
        i32.const 3
        i32.add
        local.set $i ;; i = i + 3;

        local.get $i
        i32.const 5
        i32.rem_s
        i32.const 0
        i32.eq       ;; i % 5 == 0
        if
          br $00001  ;; break if true
        end

        br $00002    ;; CONTINUE always
      end            ;; End of loop
    end              ;; End of block

17. Return Statement

    ;; return 2 * 3;

    i32.const 2
    i32.const 3
    i32.mul
    return