wat-1.239.0/.cargo_vcs_info.json0000644000000001500000000000100117770ustar { "git": { "sha1": "35f8671bce74190ef0b00ce36c399b053b490374" }, "path_in_vcs": "crates/wat" }wat-1.239.0/Cargo.lock0000644000000065340000000000100077660ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", "indexmap", "stable_deref_trait", ] [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "indexmap" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "unicode-width" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "wasm-encoder" version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" dependencies = [ "leb128fmt", "wasmparser", ] [[package]] name = "wasmparser" version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ "bitflags", "indexmap", "semver", ] [[package]] name = "wast" version = "239.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9139176fe8a2590e0fb174cdcaf373b224cb93c3dde08e4297c1361d2ba1ea5d" dependencies = [ "bumpalo", "gimli", "leb128fmt", "memchr", "unicode-width", "wasm-encoder", ] [[package]] name = "wat" version = "1.239.0" dependencies = [ "wast", ] wat-1.239.0/Cargo.toml0000644000000043000000000000100077760ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.76.0" name = "wat" version = "1.239.0" authors = ["Alex Crichton "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Rust parser for the WebAssembly Text format, WAT """ homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" documentation = "https://docs.rs/wat" readme = "README.md" license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" [package.metadata.docs.rs] all-features = true [features] component-model = ["wast/component-model"] default = ["component-model"] dwarf = ["wast/dwarf"] [lib] name = "wat" path = "src/lib.rs" [dependencies.wast] version = "239.0.0" features = ["wasm-module"] default-features = false [lints.clippy] allow_attributes_without_reason = "warn" clone_on_copy = "warn" manual_strip = "warn" map_clone = "warn" uninlined_format_args = "warn" unnecessary_cast = "warn" unnecessary_fallible_conversions = "warn" unnecessary_mut_passed = "warn" unnecessary_to_owned = "warn" [lints.clippy.all] level = "allow" priority = -1 [lints.rust] deprecated-safe-2024 = "warn" keyword_idents_2024 = "warn" missing-unsafe-on-extern = "warn" rust-2024-guarded-string-incompatible-syntax = "warn" rust-2024-incompatible-pat = "warn" rust-2024-prelude-collisions = "warn" unsafe-attr-outside-unsafe = "warn" unsafe-op-in-unsafe-fn = "warn" unsafe_code = "deny" unstable_features = "warn" unused-lifetimes = "warn" unused-macro-rules = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(fuzzing)"] wat-1.239.0/Cargo.toml.orig000064400000000000000000000017031046102023000134630ustar 00000000000000[package] name = "wat" version = "1.239.0" authors = ["Alex Crichton "] edition.workspace = true license.workspace = true readme = "README.md" repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat" documentation = "https://docs.rs/wat" description = """ Rust parser for the WebAssembly Text format, WAT """ rust-version.workspace = true [package.metadata.docs.rs] all-features = true [lints] workspace = true [dependencies] wast = { workspace = true, features = ['wasm-module'] } [features] default = ['component-model'] # Off-by-default feature to support emitting DWARF debugging information in # parsed binaries pointing back to source locations in the original `*.wat` # source. dwarf = ['wast/dwarf'] # On-by-default feature to support parsing the component model text format. component-model = ['wast/component-model'] wat-1.239.0/README.md000064400000000000000000000047711046102023000120630ustar 00000000000000

wat

A Bytecode Alliance project

A Rust parser for the WebAssembly Text Format (WAT).

Crates.io version Download docs.rs docs docs.rs docs

## Usage Add `wat` to your `Cargo.toml` ```sh $ cargo add wat ``` And then you can parse WAT to binary WebAssembly via: ```rust // Parse from a file ... let binary = wat::parse_file("./foo.wat")?; // ... or a string let wat = r#" (module (func $foo) (func (export "bar") call $foo ) ) "#; let binary = wat::parse_str(wat)?; ``` ## AST Representation The `wat` crate does not expose an AST as its goal is to provide a forever-stable interface against the `wast` crate. Using `wat` is suitable when all you want to do is translate from text-to-binary, for example parsing the input of a CLI program into the WebAssembly binary format. If instead you're interested in working with the AST of a text file or otherwise adding your own parsing to the text format you'll want to take a look at the [`wast` crate](../wast/README.md). ## Stability and WebAssembly Features Consult the [crate documentation](https://docs.rs/wat) for more information, but the general idea is this crate will not issue a semver-breaking change for breaking changes in the WAT format, either for MVP features or post-MVP features. No opt-in is required to use WebAssembly features, so using them may break if the upstream spec changes. # License This project is licensed under the Apache 2.0 license with the LLVM exception. See [LICENSE](LICENSE) for more details. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions. wat-1.239.0/src/lib.rs000064400000000000000000000365021046102023000125040ustar 00000000000000//! A Rust parser for the [WebAssembly Text format][wat] //! //! This crate contains a stable interface to the parser for the [WAT][wat] //! format of WebAssembly text files. The format parsed by this crate follows //! the [online specification][wat]. //! //! # Examples //! //! Parse an in-memory string: //! //! ``` //! # fn foo() -> wat::Result<()> { //! let wat = r#" //! (module //! (func $foo) //! //! (func (export "bar") //! call $foo //! ) //! ) //! "#; //! //! let binary = wat::parse_str(wat)?; //! // ... //! # Ok(()) //! # } //! ``` //! //! Parse an on-disk file: //! //! ``` //! # fn foo() -> wat::Result<()> { //! let binary = wat::parse_file("./foo.wat")?; //! // ... //! # Ok(()) //! # } //! ``` //! //! ## Evolution of the WAT Format //! //! WebAssembly, and the WAT format, are an evolving specification. Features are //! added to WAT, WAT changes, and sometimes WAT breaks. The policy of this //! crate is that it will always follow the [official specification][wat] for //! WAT files. //! //! Future WebAssembly features will be accepted to this parser **and they will //! not require a feature gate to opt-in**. All implemented WebAssembly features //! will be enabled at all times. Using a future WebAssembly feature in the WAT //! format may cause breakage because while specifications are in development //! the WAT syntax (and/or binary encoding) will often change. This crate will //! do its best to keep up with these proposals, but breaking textual changes //! will be published as non-breaking semver changes to this crate. //! //! ## Stability //! //! This crate is intended to be a very stable shim over the `wast` crate //! which is expected to be much more unstable. The `wast` crate contains //! AST data structures for parsing `*.wat` files and they will evolve was the //! WAT and WebAssembly specifications evolve over time. //! //! This crate is currently at version 1.x.y, and it is intended that it will //! remain here for quite some time. Breaking changes to the WAT format will be //! landed as a non-semver-breaking version change in this crate. This crate //! will always follow the [official specification for WAT][wat]. //! //! [wat]: http://webassembly.github.io/spec/core/text/index.html #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] use std::borrow::Cow; use std::fmt; use std::path::{Path, PathBuf}; use std::str; use wast::core::EncodeOptions; use wast::lexer::{Lexer, TokenKind}; use wast::parser::{self, ParseBuffer}; #[doc(inline)] pub use wast::core::GenerateDwarf; /// Parses a file on disk as a [WebAssembly Text format][wat] file, or a binary /// WebAssembly file /// /// This function will read the bytes on disk and delegate them to the /// [`parse_bytes`] function. For more information on the behavior of parsing /// see [`parse_bytes`]. /// /// # Errors /// /// For information about errors, see the [`parse_bytes`] documentation. /// /// # Examples /// /// ``` /// # fn foo() -> wat::Result<()> { /// let binary = wat::parse_file("./foo.wat")?; /// // ... /// # Ok(()) /// # } /// ``` /// /// [wat]: http://webassembly.github.io/spec/core/text/index.html pub fn parse_file(file: impl AsRef) -> Result> { Parser::new().parse_file(file) } /// Parses in-memory bytes as either the [WebAssembly Text format][wat], or a /// binary WebAssembly module. /// /// This function will attempt to interpret the given bytes as one of two /// options: /// /// * A utf-8 string which is a `*.wat` file to be parsed. /// * A binary WebAssembly file starting with `b"\0asm"` /// /// If the input is a string then it will be parsed as `*.wat`, and then after /// parsing it will be encoded back into a WebAssembly binary module. If the /// input is a binary that starts with `b"\0asm"` it will be returned verbatim. /// Everything that doesn't start with `b"\0asm"` will be parsed as a utf-8 /// `*.wat` file, returning errors as appropriate. /// /// For more information about parsing wat files, see [`parse_str`]. /// /// # Errors /// /// In addition to all of the errors that can be returned from [`parse_str`], /// this function will also return an error if the input does not start with /// `b"\0asm"` and is invalid utf-8. (failed to even try to call [`parse_str`]). /// /// # Examples /// /// ``` /// # fn foo() -> wat::Result<()> { /// // Parsing bytes that are actually `*.wat` files /// assert_eq!(&*wat::parse_bytes(b"(module)")?, b"\0asm\x01\0\0\0"); /// assert!(wat::parse_bytes(b"module").is_err()); /// assert!(wat::parse_bytes(b"binary\0file\0\that\0is\0not\0wat").is_err()); /// /// // Pass through binaries that look like real wasm files /// assert_eq!(&*wat::parse_bytes(b"\0asm\x01\0\0\0")?, b"\0asm\x01\0\0\0"); /// # Ok(()) /// # } /// ``` /// /// [wat]: http://webassembly.github.io/spec/core/text/index.html pub fn parse_bytes(bytes: &[u8]) -> Result> { Parser::new().parse_bytes(None, bytes) } /// Parses an in-memory string as the [WebAssembly Text format][wat], returning /// the file as a binary WebAssembly file. /// /// This function is intended to be a stable convenience function for parsing a /// wat file into a WebAssembly binary file. This is a high-level operation /// which does not expose any parsing internals, for that you'll want to use the /// `wast` crate. /// /// # Errors /// /// This function can fail for a number of reasons, including (but not limited /// to): /// /// * The `wat` input may fail to lex, such as having invalid tokens or syntax /// * The `wat` input may fail to parse, such as having incorrect syntactical /// structure /// * The `wat` input may contain names that could not be resolved /// /// # Examples /// /// ``` /// # fn foo() -> wat::Result<()> { /// assert_eq!(wat::parse_str("(module)")?, b"\0asm\x01\0\0\0"); /// assert!(wat::parse_str("module").is_err()); /// /// let wat = r#" /// (module /// (func $foo) /// /// (func (export "bar") /// call $foo /// ) /// ) /// "#; /// /// let binary = wat::parse_str(wat)?; /// // ... /// # Ok(()) /// # } /// ``` /// /// [wat]: http://webassembly.github.io/spec/core/text/index.html pub fn parse_str(wat: impl AsRef) -> Result> { Parser::default().parse_str(None, wat) } /// Parser configuration for transforming bytes into WebAssembly binaries. #[derive(Default)] pub struct Parser { #[cfg(feature = "dwarf")] generate_dwarf: Option, _private: (), } impl Parser { /// Creates a new parser with th default settings. pub fn new() -> Parser { Parser::default() } /// Indicates that DWARF debugging information should be generated and /// emitted by default. /// /// Note that DWARF debugging information is only emitted for textual-based /// modules. For example if a WebAssembly binary is parsed via /// [`Parser::parse_bytes`] this won't insert new DWARF information in such /// a binary. Additionally if the text format used the `(module binary ...)` /// form then no DWARF information will be emitted. #[cfg(feature = "dwarf")] pub fn generate_dwarf(&mut self, generate: GenerateDwarf) -> &mut Self { self.generate_dwarf = Some(generate); self } /// Equivalent of [`parse_file`] but uses this parser's settings. pub fn parse_file(&self, path: impl AsRef) -> Result> { self._parse_file(path.as_ref()) } fn _parse_file(&self, file: &Path) -> Result> { let contents = std::fs::read(file).map_err(|err| Error { kind: Box::new(ErrorKind::Io { err, file: Some(file.to_owned()), }), })?; match self.parse_bytes(Some(file), &contents) { // If the result here is borrowed then that means that the input // `&contents` was itself already a wasm module. We've already got // an owned copy of that so return `contents` directly after // double-checking it is indeed the same as the `bytes` return value // here. That helps avoid a copy of `bytes` via something like // `Cow::to_owned` which would otherwise copy the bytes. Ok(Cow::Borrowed(bytes)) => { assert_eq!(bytes.len(), contents.len()); assert_eq!(bytes.as_ptr(), contents.as_ptr()); Ok(contents) } Ok(Cow::Owned(bytes)) => Ok(bytes), Err(mut e) => { e.set_path(file); Err(e) } } } /// Equivalent of [`parse_bytes`] but uses this parser's settings. /// /// The `path` argument is an optional path to use when error messages are /// generated. pub fn parse_bytes<'a>(&self, path: Option<&Path>, bytes: &'a [u8]) -> Result> { if bytes.starts_with(b"\0asm") { return Ok(bytes.into()); } match str::from_utf8(bytes) { Ok(s) => self._parse_str(path, s).map(|s| s.into()), Err(_) => Err(Error { kind: Box::new(ErrorKind::Custom { msg: "input bytes aren't valid utf-8".to_string(), file: path.map(|p| p.to_owned()), }), }), } } /// Equivalent of [`parse_str`] but uses this parser's settings. /// /// The `path` argument is an optional path to use when error messages are /// generated. pub fn parse_str(&self, path: Option<&Path>, wat: impl AsRef) -> Result> { self._parse_str(path, wat.as_ref()) } fn _parse_str(&self, path: Option<&Path>, wat: &str) -> Result> { let mut _buf = ParseBuffer::new(wat).map_err(|e| Error::cvt(e, wat, path))?; #[cfg(feature = "dwarf")] _buf.track_instr_spans(self.generate_dwarf.is_some()); let mut ast = parser::parse::(&_buf).map_err(|e| Error::cvt(e, wat, path))?; let mut _opts = EncodeOptions::default(); #[cfg(feature = "dwarf")] if let Some(style) = self.generate_dwarf { _opts.dwarf(path.unwrap_or(".wat".as_ref()), wat, style); } _opts .encode_wat(&mut ast) .map_err(|e| Error::cvt(e, wat, path)) } } /// Result of [`Detect::from_bytes`] to indicate what some input bytes look /// like. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Detect { /// The input bytes look like the WebAssembly text format. WasmText, /// The input bytes look like the WebAssembly binary format. WasmBinary, /// The input bytes don't look like WebAssembly at all. Unknown, } impl Detect { /// Detect quickly if supplied bytes represent a Wasm module, /// whether binary encoded or in WAT-encoded. /// /// This briefly lexes past whitespace and comments as a `*.wat` file to see if /// we can find a left-paren. If that fails then it's probably `*.wit` instead. /// /// /// Examples /// ``` /// use wat::Detect; /// /// assert_eq!(Detect::from_bytes(r#" /// (module /// (type (;0;) (func)) /// (func (;0;) (type 0) /// nop /// ) /// ) /// "#), Detect::WasmText); /// ``` pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Detect { if bytes.as_ref().starts_with(b"\0asm") { return Detect::WasmBinary; } let text = match std::str::from_utf8(bytes.as_ref()) { Ok(s) => s, Err(_) => return Detect::Unknown, }; let lexer = Lexer::new(text); let mut iter = lexer.iter(0); while let Some(next) = iter.next() { match next.map(|t| t.kind) { Ok(TokenKind::Whitespace) | Ok(TokenKind::BlockComment) | Ok(TokenKind::LineComment) => {} Ok(TokenKind::LParen) => return Detect::WasmText, _ => break, } } Detect::Unknown } /// Returns whether this is either binary or textual wasm. pub fn is_wasm(&self) -> bool { match self { Detect::WasmText | Detect::WasmBinary => true, Detect::Unknown => false, } } } /// A convenience type definition for `Result` where the error is [`Error`] pub type Result = std::result::Result; /// Errors from this crate related to parsing WAT files /// /// An error can during example phases like: /// /// * Lexing can fail if the document is syntactically invalid. /// * A string may not be utf-8 /// * The syntactical structure of the wat file may be invalid /// * The wat file may be semantically invalid such as having name resolution /// failures #[derive(Debug)] pub struct Error { kind: Box, } #[derive(Debug)] enum ErrorKind { Wast(wast::Error), Io { err: std::io::Error, file: Option, }, Custom { msg: String, file: Option, }, } impl Error { fn cvt>(e: E, contents: &str, path: Option<&Path>) -> Error { let mut err = e.into(); if let Some(path) = path { err.set_path(path); } err.set_text(contents); Error { kind: Box::new(ErrorKind::Wast(err)), } } /// To provide a more useful error this function can be used to set /// the file name that this error is associated with. /// /// The `file` here will be stored in this error and later rendered in the /// `Display` implementation. pub fn set_path>(&mut self, file: P) { let file = file.as_ref(); match &mut *self.kind { ErrorKind::Wast(e) => e.set_path(file), ErrorKind::Custom { file: f, .. } => *f = Some(file.to_owned()), ErrorKind::Io { file: f, .. } => *f = Some(file.to_owned()), } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &*self.kind { ErrorKind::Wast(err) => err.fmt(f), ErrorKind::Custom { msg, file, .. } => match file { Some(file) => { write!(f, "failed to parse `{}`: {}", file.display(), msg) } None => msg.fmt(f), }, ErrorKind::Io { err, file, .. } => match file { Some(file) => { write!(f, "failed to read from `{}`", file.display()) } None => err.fmt(f), }, } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &*self.kind { ErrorKind::Wast(_) => None, ErrorKind::Custom { .. } => None, ErrorKind::Io { err, .. } => Some(err), } } } #[cfg(test)] mod test { use super::*; #[test] fn test_set_path() { let mut e = parse_bytes(&[0xFF]).unwrap_err(); e.set_path("foo"); assert_eq!( e.to_string(), "failed to parse `foo`: input bytes aren't valid utf-8" ); let e = parse_file("_does_not_exist_").unwrap_err(); assert!( e.to_string() .starts_with("failed to read from `_does_not_exist_`") ); let mut e = parse_bytes("()".as_bytes()).unwrap_err(); e.set_path("foo"); assert_eq!( e.to_string(), "expected valid module field\n --> foo:1:2\n |\n 1 | ()\n | ^" ); } }