On this page

Basics

Before we can get started writing WAT code, there are some important parts you need to understand. The syntax used is not like most other programming languages, there are some unusual limitations, and you need to think a little differently when writing your code.

These basic parts are the bedrock of what makes WAT work and it is something you will need to understand before you can move on to other sections.

Types

In other programming languages there are a wide range of types to choose from. Integers, floating point numbers, strings, date classes, and others. But in WAT you only have a limited number you can use.

i32 32 bit unsigned integer 0 to 2^32
i64 64 bit unsigned integer 0 to 2^64
f32 32 bit floating point number
f64 64 bit floating point number
v128 128 bit vector containing either,
2x 64 bit floats
4x 32 bit floats
2x 64 bit unsigned integer
4x 32 bit unsigned integer
8x 16 bit unsigned integer
16x 8 bit unsigned integer
funcref Reference to a function

The integers are unsigned, which means they do not have a negative bit, they store all the bits as they are. However there are instructions in WAT that treat the integers as being either signed or unsigned, so you can use negative numbers.

You use the i32 type as a Boolean, with the value 0 meaning FALSE and any other value meaning TRUE.

There is no string type, which can seem odd, but there is a memory area we can create and use to hold text data in whatever format we need.

This does all seem very limiting but it is how things look at a machine code level. These are the building blocks you use to make more complex structures, and it is how data is seen and used at the lowest of levels.

Stack Machine

At a machine code level, to use instructions like ADD, you would either use a CPU register or a memory location. This is just some place to put the parameters before calling the instruction and somewhere to store the result. Because Web Assembly is machine independent, and each machine can have different CPUs and different registers, an alternative method was needed to do the same sort of thing.

A stack machine is used to temporarily store parameters and results for instructions and functions in an easy to understand way. It may not be the way the data is handled on the running computer, after it has loaded in the WASM file, but from a WAT point of view it is a simple list of data that you can put data on and take off. It is very different to anything you may have used before, but it is easy to use once you understand what is going on.

To visualise this, imagine a list of items starting at the bottom and going up. When you want to add something to the stack you PUSH it on to the top. When you want to take something off, you POP it off from the top. It is a Last In, First Out (LIFO) principle.

12 Top of Stack
502
332 Bottom of Stack

PUSH the value 9123 onto the stack.

>> 9123 Top of Stack
12
502
332 Bottom of Stack

POP the top most value off the stack.

<< 9123
12 Top of Stack
502
332 Bottom of Stack

You can add any data type to the stack, however they need to match the requirements of the instruction or function you want to call. For example, i32.add requires two i32 values on the stack, so using i64 will create a problem.

The best way to understand this is by showing an example. Let's say we want to perform the following calculation in WAT.

= 100 - 42

To perform this operation we need to use the i32.sub instruction. This requires two values on the stack. This is done in the following way.

;; Push 101 onto the stack
i32.const 101

;; Push 42 onto the stack
i32.const 42

;; Subtract 42 from 101
i32.sub

At the start the stack is empty. We PUSH the value 101 and 42 onto the stack.

42 Top of Stack
101 Bottom of Stack

The i32.sub instruction will POP the last two stack items off the stack, subtract 42 from 101, and then PUSH the result back onto the stack at the top.

59 Top of Stack

The order you PUSH items onto the stack is important. If you had changed the order around in the example above then you would have performed 42 - 101 instead.

S-Expressions

The information in a WASM file is in a tree-like format, with a parent object containing sub child objects, and with them also containing sub child objects. This is shown in WAT using symbolic expressions (S-Expressions for short). It looks something like this.

(parent
  (sub-child-1
    (sub-sub-child-1.1)
    (sub-sub-child-1.2)
    (sub-sub-child-1.3)
  )
  (sub-child-2)
)

You surround the object with parentheses (...). There needs to be a label, instruction, keyword, or something after the first ( character before you include other objects. This is what the tree-like structure for the above example looks like.

We can write the same set of operations in a number of different ways in WAT. The first example shows it written without any S-Expressions.

local.get $first
local.get $second
i32.sub

We get the $first and $second parameters and push them onto the stack. We subtract the $second value from the $first and push the result onto the stack. For the next example we will do the same thing but on one line using an S-Expression.

(i32.sub (local.get $first) (local.get $second))

It looks backwards. It seems that we are calling i32.sub before putting the parameters onto the stack. We can open this up a little to see how else it can be written.

;; Open it up #1
(i32.sub
  (local.get $first)
  (local.get $second)
)

;; Open it up #2
(
  i32.sub
  (
    local.get $first
  )
  (
    local.get
    $second
  )
)

To understand what is happening here we need to look at it in a tree-like way. Take a look at the following diagram.

The parent node is the i32.sub instruction, which has the two sub-child objects, the first being on the left and the second on the right. We are in a way saying that the parent object needs to be processed, and to do so we need to process its sub-children object first, and in the given order.

All these different ways of writing the operation doesn't really matter. The WASM code that is created looks exactly the same. You can write it in whichever way seems best for you.

Comments

You will need to add comments to your WAT programming, as it can be difficult to know what you are trying to do just by looking at the code. There are two ways to add a comment.

;; This is a one line comment

The comment starts with two semicolons ;; and ends when the line breaks. Ideal for a small comment.


(; This
   is
   multilined
   comment
;)

This comment starts with a parentheses and semicolon (; and can continue for many lines until it finds the ending ;) characters. This is good for a large body of text with detailed comments.

All comments do not end up in the final WASM file.

Naming Identifiers

When naming parameters, functions, local variables, loops, and other things within WAT, you have to start them with a $ character. After that it can only contain the following ASCII characters (and no spaces).

0 to 9
A to Z
a to z and any of the following,
! # $ % & ' * + - / : < = > ? @ \ ^ _ ` | ~

(func $add (param $first i32) (param $second i32) (result i32)...)

Here the function and parameter names all start with the $ character.