wit_text/
lib.rs

1//! A crate to parse the textual format for WebAssembly interface types.
2//!
3//! This crate is a work-in-progress and should expect to have a good deal of
4//! change as the official proposal evolves. The main purpose of this crate is
5//! to parse a textual file into a binary representation, and the parsing
6//! includes parsing of all of the WebAssembly core types/syntax as well.
7
8#![deny(missing_docs)]
9
10use anyhow::{bail, Context};
11use std::borrow::Cow;
12use std::path::Path;
13use std::str;
14use wast::parser::ParseBuffer;
15
16mod ast;
17mod binary;
18mod resolve;
19pub use ast::*;
20
21/// Parses a `file` on the filesystem as a textual representation of WebAssembly
22/// Interface Types, returning the binary representation of the module.
23///
24/// Note that the `file` could either be a valid `*.wat` or `*.wasm`
25/// file. In the `*.wasm` case the bytes are passed through unmodified.
26///
27/// # Errors
28///
29/// For information about errors, see the [`parse_bytes`] documentation.
30pub fn parse_file(file: impl AsRef<Path>) -> anyhow::Result<Vec<u8>> {
31    _parse_file(file.as_ref())
32}
33
34fn _parse_file(file: &Path) -> anyhow::Result<Vec<u8>> {
35    let contents = std::fs::read(file).context(format!("failed to read `{}`", file.display()))?;
36    match parse_bytes(&contents) {
37        Ok(bytes) => Ok(bytes.into_owned()),
38        Err(mut e) => {
39            if let Some(e) = e.downcast_mut::<wast::Error>() {
40                e.set_path(file);
41            }
42            Err(e)
43        }
44    }
45}
46
47/// Parses in-memory bytes as either the text format or a binary WebAssembly
48/// module.
49///
50/// This function will attempt to interpret the given bytes as one of two
51/// options:
52///
53/// * A utf-8 string which is a `*.wat` file to be parsed.
54/// * A binary WebAssembly file starting with `b"\0asm"`
55///
56/// If the input is a string then it will be parsed as `*.wat`, and then after
57/// parsing it will be encoded back into a WebAssembly binary module. If the
58/// input is a binary that starts with `b"\0asm"` it will be returned verbatim.
59/// Everything that doesn't start with `b"\0asm"` will be parsed as a utf-8
60/// `*.wat` file, returning errors as appropriate.
61///
62/// # Errors
63///
64/// In addition to all of the errors that can be returned from [`parse_str`],
65/// this function will also return an error if the input does not start with
66/// `b"\0asm"` and is invalid utf-8. (failed to even try to call [`parse_str`]).
67pub fn parse_bytes(bytes: &[u8]) -> anyhow::Result<Cow<'_, [u8]>> {
68    if bytes.starts_with(b"\0asm") {
69        return Ok(bytes.into());
70    }
71    let result = match str::from_utf8(bytes) {
72        Ok(s) => _parse_str(s)?,
73        Err(_) => bail!("input bytes aren't valid utf-8"),
74    };
75    Ok(result.into())
76}
77
78/// Parses an in-memory string as the text format, returning the file as a
79/// binary WebAssembly file.
80///
81/// This function is intended to be a stable convenience function for parsing a
82/// `*.wat` file into a WebAssembly binary. This is a high-level operation which
83/// does not expose any parsing internals, for that you'll want to use the
84/// [`Module`] type and the `wast` crate.
85///
86/// # Errors
87///
88/// This function can fail for a number of reasons, including (but not limited
89/// to):
90///
91/// * The `wat` input may fail to lex, such as having invalid tokens or syntax
92/// * The `wat` input may fail to parse, such as having incorrect syntactical
93///   structure
94/// * The `wat` input may contain names that could not be resolved
95///
96pub fn parse_str(wat: impl AsRef<str>) -> Result<Vec<u8>, wast::Error> {
97    _parse_str(wat.as_ref())
98}
99
100fn _parse_str(wat: &str) -> Result<Vec<u8>, wast::Error> {
101    let adjust = |mut err: wast::Error| {
102        err.set_text(wat);
103        err
104    };
105    let buf = ParseBuffer::new(&wat).map_err(adjust)?;
106    let mut ast = wast::parser::parse::<Wat>(&buf).map_err(adjust)?;
107    ast.module.encode().map_err(adjust)
108}