<p>IIRC the WebAssembly text format (.wat) is not able to express everything .wasm can, such as <s>binary</s> (custom) sections. I'm not sure about the current state of WebAssembly text and binary formats now though.</p>
<p>I asked here recently about how I could add source locations (including the full call stack) to thrown exceptions in C++ and got <a href="https://langdev.stackexchange.com/a/1738/606">this answer</a> suggesting the <code>#line</code> directive (which can be useful in my case) and the use of <code>libunwind</code> to collect the call stack.</p>
<p>I was thinking of researching on the WebAssembly target for avoiding possible name clash and reserved keywords
issues and also because I'm not experienced with C++.</p>
<p>I'm wondering if it's feasible to target .wat since writing the binary manually requires more work. Did any language do this in practice? I know Lobster targets the binary format (.wasm), but I use another language for my type checker, C#, and Lobster's writer is written in C++, so it may be problematic to port it.</p>
<hr />
<p>It looks like C# does have a library for writing .wasm: <a href="https://github.com/RyanLamansky/dotnet-webassembly" rel="nofollow noreferrer">https://github.com/RyanLamansky/dotnet-webassembly</a></p>

<exceptions><webassembly>
Is it fine to target .wat instead of .wasm and use wat2wasm?
CC BY-SA 4.0
2023-06-29T23:57:54.297Z
Tangential to the question, but if you target wasm, you might face [this issue](https://langdev.stackexchange.com/q/1064/160).
Bubbler
CC BY-SA 4.0
2024-02-24T17:48:32.953Z
Are either of the existing answers acceptable, or does a new answer need to be written?
Corbin
<p>Well, the main compiler for my programming language, the <a href="https://github.com/FlatAssembler/AECforWebAssembly.git" rel="noreferrer">AEC-to-WebAssembly compiler</a>, does output <code>.wat</code> and use <code>wat2wasm</code>. My programming language doesn't have exceptions, though, nor would I know how to implement them.</p>

CC BY-SA 4.0
2023-06-30T00:41:04.370Z
2023-06-30T00:41:04.370Z
FlatAssembler
<p>I think yes, the compiler should produce <code>.wat</code> format.</p>
<p>My Valkyrie language now uses the <code>.wat</code> format, and a long time ago it directly output <code>.wasm</code>.</p>
<p>Next I will explain the reasons for this change, I hope it will be helpful to you.</p>
<hr />
<p>As preparatory knowledge, first introduce some core libraries and concepts I use
​</p>
<ul>
<li><a href="https://crates.io/crates/wasm-encoder" rel="noreferrer">wasm-encoder</a>: The widely used wasm linker, which produces <code>.wasm</code></li>
<li><a href="https://github.com/nyar-vm/nyar-vm/tree/644b0a0321bdab74262e49ce455565b05dee41c2/projects/wast-encoder" rel="noreferrer">wast-encoder</a>: The new linker I implemented, output is <code>.wat</code></li>
<li><a href="https://crates.io/crates/wasmparser" rel="noreferrer">wasmparser</a>: The widely used wasm parser and validator, supporting both text and binary.</li>
<li><a href="https://crates.io/crates/wasm-opt" rel="noreferrer">wasm-opt</a>: Widely used optimizer, but does not support the latest wit components.</li>
</ul>
<p>A long time ago I used <code>wasm-encoder</code> to directly encode my types, and <code>wasm-opt</code> to optimize the binary, and everything was great.</p>
<p>Until the introduction of wit and gc, various tool chains were unable to parse the gc instructions and wit definitions emitted by wasm-encoder. Only the chrome browser could execute the binary I output, but it was very difficult to debug.</p>
<p>This has been going on for a long time and will continue to be for a long time. My toolchain is paralyzed.</p>
<p>In this context I developed <code>wast-encoder</code>, which handles the linking process and finally outputs <code>.wat</code>. This is essentially a linker, not an encoder.</p>
<p>In the wit component, the wasm modules need to be instantiated in a specific order, and each newly defined type must reference an already defined type. This is beyond the scope of a simple coder.</p>
<p>Another reason is that the instructions issued by the encoder erase the information in the source language. Even if chrome tells me which line wasm reports an error, I don't know what went wrong.</p>
<p><code>wasm-encoder</code> emits code similar to this (decompiled)</p>
<pre><code>(component
  (import &quot;wasi:io/error@0.2.0&quot; (instance (;1;) (type 1)))
  (alias export 1 &quot;error&quot; (type (;2;)))
  (type (;3;)
    (instance
      (export (;0;) &quot;output-stream&quot; (type (sub resource)))
      (alias outer 1 2 (type (;1;)))
      (export (;2;) &quot;error&quot; (type (eq 1)))
      (type (;3;) (own 2))
      (type (;4;) (variant (case &quot;last-operation-failed&quot; 3) (case &quot;closed&quot;)))
      (export (;5;) &quot;stream-error&quot; (type (eq 4)))
      (type (;6;) (borrow 0))
      (type (;7;) (list u8))
      (type (;8;) (result (error 5)))
      (type (;9;) (func (param &quot;self&quot; 6) (param &quot;contents&quot; 7) (result 8)))
      (export (;0;) &quot;[method]output-stream.blocking-write-and-flush&quot; (func (type 9)))
    )
  )
  (import &quot;wasi:io/streams@0.2.0&quot; (instance (;2;) (type 3)))
  (alias export 2 &quot;output-stream&quot; (type (;4;)))
  (core instance (;4;)
    (export &quot;get-stdin&quot; (func 2))
  )
  (alias export 5 &quot;get-stdout&quot; (func (;5;)))
)
</code></pre>
<p><code>wast-encoder</code> emits more understandable code, although this is longer.</p>
<pre><code>(component <span class="math-container">$Root
    (import "wasi:io/error@0.2.0" (instance $</span>wasi:io/error@0.2.0
        ;; class std::io::Error
        (export &quot;error&quot; (type (sub resource)))
    ))
    (alias export <span class="math-container">$wasi:io/error@0.2.0 "error" (type $</span>io-error))

    ;; variant std::io::StreamError
    (type <span class="math-container">$stream-error (variant
    (case "last-operation-failed" (own $</span>io-error))
        (case &quot;closed&quot;)
    ))

    (import &quot;wasi:io/streams@0.2.0&quot; (instance <span class="math-container">$wasi:io/streams@0.2.0
    (export $</span>output-stream &quot;output-stream&quot; (type (sub resource)))

        (alias outer <span class="math-container">$Root $</span>io-error (type <span class="math-container">$io-error))
    (export $</span>stream-error0 &quot;stream-error&quot; (type (eq <span class="math-container">$stream-error)))
    (export $</span>stream-error1 &quot;alias0&quot; (type (eq $stream-error)))

        (export &quot;[method]output-stream.blocking-write-and-flush&quot;
            ;; class std::io::OutputStream
            ;; method blocking-write-and-flush(self) -&gt; Result&lt;(), std::io::Error&gt;
            (func (param &quot;self&quot; (borrow <span class="math-container">$output-stream)) (param "contents" (list u8)) (result (result (error $</span>stream-error0))))
        )
    ))
    (alias export <span class="math-container">$wasi:io/streams@0.2.0 "output-stream" (type $</span>output-stream))
    (alias export <span class="math-container">$wasi:io/streams@0.2.0 "alias0" (type $</span>output-stream0))

    (import &quot;wasi:cli/stdout@0.2.0&quot; (instance <span class="math-container">$wasi:cli/stdout@0.2.0
    (alias outer $</span>Root <span class="math-container">$output-stream (type $</span>output-stream))
        (export &quot;get-stdout&quot;
            ;; class std::io::OutputStream
            ;; static stdout() -&gt; Self
            (func (result (own $output-stream)))
        )
    ))
)
</code></pre>
<p>The size issue is not very important. Although <code>wasm-opt</code> does not currently support <code>gc</code> and <code>wit</code>, compact binaries will be obtained in the future.</p>
<p>In contrast, text with source language information and structure is more helpful for debugging.</p>
<hr />
<p>Finally, focus your scenario in question.</p>
<p>There is currently no specification standard that can emit raw stack information. I know that LLDB can add some custom instructions, and chrome can recognize and jump directly, but this is non-standard, the tool chain support is limited, and the future is uncertain.</p>
<p>I have a proposal for tools to recognize such information (this is legal wasm, even now).
​</p>
<pre><code>(func $always-one
    (@name &quot;function-name-in-source-language&quot;)
    (@file &quot;user/language/source.vk&quot;)
    (@line 12)
    (@column 4)
    (result i32) 
    (i32.const 0)
)
</code></pre>
<p>In any case, I hope to use the solution within the wasm standard, rather than the solution customized by various tools.</p>

CC BY-SA 4.0
2024-02-24T06:53:35.200Z
2024-02-24T06:53:35.200Z
Aster
# Is it fine to target .wat instead of .wasm and use wat2wasm?
Posted by: username_0 on 2023-06-29
IIRC the WebAssembly text format (.wat) is not able to express everything .wasm can, such as ~~binary~~ (custom) sections. I'm not sure about the current state of WebAssembly text and binary formats now though.

I asked here recently about how I could add source locations (including the full call stack) to thrown exceptions in C++ and got [this answer](https://langdev.stackexchange.com/a/1738/606) suggesting the `#line` directive (which can be useful in my case) and the use of `libunwind` to collect the call stack.

I was thinking of researching on the WebAssembly target for avoiding possible name clash and reserved keywords
issues and also because I'm not experienced with C++.

I'm wondering if it's feasible to target .wat since writing the binary manually requires more work. Did any language do this in practice? I know Lobster targets the binary format (.wasm), but I use another language for my type checker, C#, and Lobster's writer is written in C++, so it may be problematic to port it.

---

It looks like C# does have a library for writing .wasm: <https://github.com/RyanLamansky/dotnet-webassembly>
**Tags:** exceptions webassembly
> **Comments:**
> **username_1**: Tangential to the question, but if you target wasm, you might face [this issue](https://langdev.stackexchange.com/q/1064/160).  
> **username_2**: Are either of the existing answers acceptable, or does a new answer need to be written?  
---
## 2 answers
### Answer by username_4 on 2024-02-24. Score: 5 | Accepted answer
I think yes, the compiler should produce .wat format.
My Valkyrie language now uses the .wat format, and a long time ago it directly output .wasm.
Next I will explain the reasons for this change, I hope it will be helpful to you.

As preparatory knowledge, first introduce some core libraries and concepts I use
​

wasm-encoder: The widely used wasm linker, which produces .wasm
wast-encoder: The new linker I implemented, output is .wat
wasmparser: The widely used wasm parser and validator, supporting both text and binary.
wasm-opt: Widely used optimizer, but does not support the latest wit components.

A long time ago I used wasm-encoder to directly encode my types, and wasm-opt to optimize the binary, and everything was great.
Until the introduction of wit and gc, various tool chains were unable to parse the gc instructions and wit definitions emitted by wasm-encoder. Only the chrome browser could execute the binary I output, but it was very difficult to debug.
This has been going on for a long time and will continue to be for a long time. My toolchain is paralyzed.
In this context I developed wast-encoder, which handles the linking process and finally outputs .wat. This is essentially a linker, not an encoder.
In the wit component, the wasm modules need to be instantiated in a specific order, and each newly defined type must reference an already defined type. This is beyond the scope of a simple coder.
Another reason is that the instructions issued by the encoder erase the information in the source language. Even if chrome tells me which line wasm reports an error, I don't know what went wrong.
wasm-encoder emits code similar to this (decompiled)
(component
  (import "wasi:io/error@0.2.0" (instance (;1;) (type 1)))
  (alias export 1 "error" (type (;2;)))
  (type (;3;)
    (instance
      (export (;0;) "output-stream" (type (sub resource)))
      (alias outer 1 2 (type (;1;)))
      (export (;2;) "error" (type (eq 1)))
      (type (;3;) (own 2))
      (type (;4;) (variant (case "last-operation-failed" 3) (case "closed")))
      (export (;5;) "stream-error" (type (eq 4)))
      (type (;6;) (borrow 0))
      (type (;7;) (list u8))
      (type (;8;) (result (error 5)))
      (type (;9;) (func (param "self" 6) (param "contents" 7) (result 8)))
      (export (;0;) "[method]output-stream.blocking-write-and-flush" (func (type 9)))
    )
  )
  (import "wasi:io/streams@0.2.0" (instance (;2;) (type 3)))
  (alias export 2 "output-stream" (type (;4;)))
  (core instance (;4;)
    (export "get-stdin" (func 2))
  )
  (alias export 5 "get-stdout" (func (;5;)))
)

wast-encoder emits more understandable code, although this is longer.
(component $Root
    (import "wasi:io/error@0.2.0" (instance $wasi:io/error@0.2.0
        ;; class std::io::Error
        (export "error" (type (sub resource)))
    ))
    (alias export $wasi:io/error@0.2.0 "error" (type $io-error))

    ;; variant std::io::StreamError
    (type $stream-error (variant
    (case "last-operation-failed" (own $io-error))
        (case "closed")
    ))

    (import "wasi:io/streams@0.2.0" (instance $wasi:io/streams@0.2.0
    (export $output-stream "output-stream" (type (sub resource)))

        (alias outer $Root $io-error (type $io-error))
    (export $stream-error0 "stream-error" (type (eq $stream-error)))
    (export $stream-error1 "alias0" (type (eq $stream-error)))

        (export "[method]output-stream.blocking-write-and-flush"
            ;; class std::io::OutputStream
            ;; method blocking-write-and-flush(self) -> Result<(), std::io::Error>
            (func (param "self" (borrow $output-stream)) (param "contents" (list u8)) (result (result (error $stream-error0))))
        )
    ))
    (alias export $wasi:io/streams@0.2.0 "output-stream" (type $output-stream))
    (alias export $wasi:io/streams@0.2.0 "alias0" (type $output-stream0))

    (import "wasi:cli/stdout@0.2.0" (instance $wasi:cli/stdout@0.2.0
    (alias outer $Root $output-stream (type $output-stream))
        (export "get-stdout"
            ;; class std::io::OutputStream
            ;; static stdout() -> Self
            (func (result (own $output-stream)))
        )
    ))
)

The size issue is not very important. Although wasm-opt does not currently support gc and wit, compact binaries will be obtained in the future.
In contrast, text with source language information and structure is more helpful for debugging.

Finally, focus your scenario in question.
There is currently no specification standard that can emit raw stack information. I know that LLDB can add some custom instructions, and chrome can recognize and jump directly, but this is non-standard, the tool chain support is limited, and the future is uncertain.
I have a proposal for tools to recognize such information (this is legal wasm, even now).
​
(func $always-one
    (@name "function-name-in-source-language")
    (@file "user/language/source.vk")
    (@line 12)
    (@column 4)
    (result i32) 
    (i32.const 0)
)

In any case, I hope to use the solution within the wasm standard, rather than the solution customized by various tools.
---
### Answer by username_3 on 2023-06-30. Score: 5
Well, the main compiler for my programming language, the [AEC-to-WebAssembly compiler](https://github.com/FlatAssembler/AECforWebAssembly.git), does output `.wat` and use `wat2wasm`. My programming language doesn't have exceptions, though, nor would I know how to implement them.
---
