wast/
lib.rs

1//! A crate for low-level parsing of the WebAssembly text formats: WAT and WAST.
2//!
3//! This crate is intended to be a low-level detail of the `wat` crate,
4//! providing a low-level parsing API for parsing WebAssembly text format
5//! structures. The API provided by this crate is very similar to
6//! [`syn`](https://docs.rs/syn) and provides the ability to write customized
7//! parsers which may be an extension to the core WebAssembly text format. For
8//! more documentation see the [`parser`] module.
9//!
10//! # High-level Overview
11//!
12//! This crate provides a few major pieces of functionality
13//!
14//! * [`lexer`] - this is a raw lexer for the wasm text format. This is not
15//!   customizable, but if you'd like to iterate over raw tokens this is the
16//!   module for you. You likely won't use this much.
17//!
18//! * [`parser`] - this is the workhorse of this crate. The [`parser`] module
19//!   provides the [`Parse`][] trait primarily and utilities
20//!   around working with a [`Parser`](`parser::Parser`) to parse streams of
21//!   tokens.
22//!
23//! * [`Module`](crate::core::Module) - this contains an Abstract Syntax Tree
24//!   (AST) of the WebAssembly Text format (WAT) as well as the unofficial WAST
25//!   format. This also has a [`Module::encode`](crate::core::Module::encode)
26//!   method to emit a module in its binary form.
27//!
28//! # Stability and WebAssembly Features
29//!
30//! This crate provides support for many in-progress WebAssembly features such
31//! as reference types, multi-value, etc. Be sure to check out the documentation
32//! of the [`wast` crate](https://docs.rs/wast) for policy information on crate
33//! stability vs WebAssembly Features. The tl;dr; version is that this crate
34//! will issue semver-non-breaking releases which will break the parsing of the
35//! text format. This crate, unlike `wast`, is expected to have numerous Rust
36//! public API changes, all of which will be accompanied with a semver-breaking
37//! release.
38//!
39//! # Compile-time Cargo features
40//!
41//! This crate has a `wasm-module` feature which is turned on by default which
42//! includes all necessary support to parse full WebAssembly modules. If you
43//! don't need this (for example you're parsing your own s-expression format)
44//! then this feature can be disabled.
45//!
46//! This crate also has an off-by-default `dwarf` feature which enables using
47//! [`core::EncodeOptions::dwarf`] to embed DWARF debugging information in generated
48//! binaries.
49//!
50//! [`Parse`]: parser::Parse
51//! [`LexError`]: lexer::LexError
52
53#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
54#![cfg_attr(docsrs, feature(doc_auto_cfg))]
55
56/// A macro to create a custom keyword parser.
57///
58/// This macro is invoked in one of two forms:
59///
60/// ```
61/// // keyword derived from the Rust identifier:
62/// wast::custom_keyword!(foo);
63///
64/// // or an explicitly specified string representation of the keyword:
65/// wast::custom_keyword!(my_keyword = "the-wasm-keyword");
66/// ```
67///
68/// This can then be used to parse custom keyword for custom items, such as:
69///
70/// ```
71/// use wast::parser::{Parser, Result, Parse};
72///
73/// struct InlineModule<'a> {
74///     inline_text: &'a str,
75/// }
76///
77/// mod kw {
78///     wast::custom_keyword!(inline);
79/// }
80///
81/// // Parse an inline string module of the form:
82/// //
83/// //    (inline "(module (func))")
84/// impl<'a> Parse<'a> for InlineModule<'a> {
85///     fn parse(parser: Parser<'a>) -> Result<Self> {
86///         parser.parse::<kw::inline>()?;
87///         Ok(InlineModule {
88///             inline_text: parser.parse()?,
89///         })
90///     }
91/// }
92/// ```
93///
94/// Note that the keyword name can only start with a lower-case letter, i.e. 'a'..'z'.
95#[macro_export]
96macro_rules! custom_keyword {
97    ($name:ident) => {
98        $crate::custom_keyword!($name = stringify!($name));
99    };
100    ($name:ident = $kw:expr) => {
101        #[allow(non_camel_case_types)]
102        #[allow(missing_docs)]
103        #[derive(Debug, Copy, Clone)]
104        pub struct $name(#[allow(dead_code)] pub $crate::token::Span);
105
106        impl<'a> $crate::parser::Parse<'a> for $name {
107            fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
108                parser.step(|c| {
109                    if let Some((kw, rest)) = c.keyword()? {
110                        if kw == $kw {
111                            return Ok(($name(c.cur_span()), rest));
112                        }
113                    }
114                    Err(c.error(concat!("expected keyword `", $kw, "`")))
115                })
116            }
117        }
118
119        impl $crate::parser::Peek for $name {
120            fn peek(cursor: $crate::parser::Cursor<'_>) -> $crate::parser::Result<bool> {
121                Ok(if let Some((kw, _rest)) = cursor.keyword()? {
122                    kw == $kw
123                } else {
124                    false
125                })
126            }
127
128            fn display() -> &'static str {
129                concat!("`", $kw, "`")
130            }
131        }
132    };
133}
134
135/// A macro for defining custom reserved symbols.
136///
137/// This is like `custom_keyword!` but for reserved symbols (`Token::Reserved`)
138/// instead of keywords (`Token::Keyword`).
139///
140/// ```
141/// use wast::parser::{Parser, Result, Parse};
142///
143/// // Define a custom reserved symbol, the "spaceship" operator: `<=>`.
144/// wast::custom_reserved!(spaceship = "<=>");
145///
146/// /// A "three-way comparison" like `(<=> a b)` that returns -1 if `a` is less
147/// /// than `b`, 0 if they're equal, and 1 if `a` is greater than `b`.
148/// struct ThreeWayComparison<'a> {
149///     lhs: wast::core::Expression<'a>,
150///     rhs: wast::core::Expression<'a>,
151/// }
152///
153/// impl<'a> Parse<'a> for ThreeWayComparison<'a> {
154///     fn parse(parser: Parser<'a>) -> Result<Self> {
155///         parser.parse::<spaceship>()?;
156///         let lhs = parser.parse()?;
157///         let rhs = parser.parse()?;
158///         Ok(ThreeWayComparison { lhs, rhs })
159///     }
160/// }
161/// ```
162#[macro_export]
163macro_rules! custom_reserved {
164    ($name:ident) => {
165        $crate::custom_reserved!($name = stringify!($name));
166    };
167    ($name:ident = $rsv:expr) => {
168        #[allow(non_camel_case_types)]
169        #[allow(missing_docs)]
170        #[derive(Debug)]
171        pub struct $name(pub $crate::token::Span);
172
173        impl<'a> $crate::parser::Parse<'a> for $name {
174            fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
175                parser.step(|c| {
176                    if let Some((rsv, rest)) = c.reserved()? {
177                        if rsv == $rsv {
178                            return Ok(($name(c.cur_span()), rest));
179                        }
180                    }
181                    Err(c.error(concat!("expected reserved symbol `", $rsv, "`")))
182                })
183            }
184        }
185
186        impl $crate::parser::Peek for $name {
187            fn peek(cursor: $crate::parser::Cursor<'_>) -> Result<bool> {
188                if let Some((rsv, _rest)) = cursor.reserved()? {
189                    Ok(rsv == $rsv)
190                } else {
191                    Ok(false)
192                }
193            }
194
195            fn display() -> &'static str {
196                concat!("`", $rsv, "`")
197            }
198        }
199    };
200}
201
202/// A macro, like [`custom_keyword`], to create a type which can be used to
203/// parse/peek annotation directives.
204///
205/// Note that when you're parsing custom annotations it can be somewhat tricky
206/// due to the nature that most of them are skipped. You'll want to be sure to
207/// consult the documentation of [`Parser::register_annotation`][register] when
208/// using this macro.
209///
210/// # Examples
211///
212/// To see an example of how to use this macro, let's invent our own syntax for
213/// the [producers section][section] which looks like:
214///
215/// ```wat
216/// (@producer "wat" "1.0.2")
217/// ```
218///
219/// Here, for simplicity, we'll assume everything is a `processed-by` directive,
220/// but you could get much more fancy with this as well.
221///
222/// ```
223/// # use wast::*;
224/// # use wast::parser::*;
225///
226/// // First we define the custom annotation keyword we're using, and by
227/// // convention we define it in an `annotation` module.
228/// mod annotation {
229///     wast::annotation!(producer);
230/// }
231///
232/// struct Producer<'a> {
233///     name: &'a str,
234///     version: &'a str,
235/// }
236///
237/// impl<'a> Parse<'a> for Producer<'a> {
238///     fn parse(parser: Parser<'a>) -> Result<Self> {
239///         // Remember that parser conventionally parse the *interior* of an
240///         // s-expression, so we parse our `@producer` annotation and then we
241///         // parse the payload of our annotation.
242///         parser.parse::<annotation::producer>()?;
243///         Ok(Producer {
244///             name: parser.parse()?,
245///             version: parser.parse()?,
246///         })
247///     }
248/// }
249/// ```
250///
251/// Note though that this is only half of the parser for annotations. The other
252/// half is calling the [`register_annotation`][register] method at the right
253/// time to ensure the parser doesn't automatically skip our `@producer`
254/// directive. Note that we *can't* call it inside the `Parse for Producer`
255/// definition because that's too late and the annotation would already have
256/// been skipped.
257///
258/// Instead we'll need to call it from a higher level-parser before the
259/// parenthesis have been parsed, like so:
260///
261/// ```
262/// # use wast::*;
263/// # use wast::parser::*;
264/// struct Module<'a> {
265///     fields: Vec<ModuleField<'a>>,
266/// }
267///
268/// impl<'a> Parse<'a> for Module<'a> {
269///     fn parse(parser: Parser<'a>) -> Result<Self> {
270///         // .. parse module header here ...
271///
272///         // register our custom `@producer` annotation before we start
273///         // parsing the parentheses of each field
274///         let _r = parser.register_annotation("producer");
275///
276///         let mut fields = Vec::new();
277///         while !parser.is_empty() {
278///             fields.push(parser.parens(|p| p.parse())?);
279///         }
280///         Ok(Module { fields })
281///     }
282/// }
283///
284/// enum ModuleField<'a> {
285///     Producer(Producer<'a>),
286///     // ...
287/// }
288/// # struct Producer<'a>(&'a str);
289/// # impl<'a> Parse<'a> for Producer<'a> {
290/// #   fn parse(parser: Parser<'a>) -> Result<Self> { Ok(Producer(parser.parse()?)) }
291/// # }
292/// # mod annotation { wast::annotation!(producer); }
293///
294/// impl<'a> Parse<'a> for ModuleField<'a> {
295///     fn parse(parser: Parser<'a>) -> Result<Self> {
296///         // and here `peek` works and our delegated parsing works because the
297///         // annotation has been registered.
298///         if parser.peek::<annotation::producer>()? {
299///             return Ok(ModuleField::Producer(parser.parse()?));
300///         }
301///
302///         // .. typically we'd parse other module fields here...
303///
304///         Err(parser.error("unknown module field"))
305///     }
306/// }
307/// ```
308///
309/// [register]: crate::parser::Parser::register_annotation
310/// [section]: https://github.com/WebAssembly/tool-conventions/blob/master/ProducersSection.md
311#[macro_export]
312macro_rules! annotation {
313    ($name:ident) => {
314        $crate::annotation!($name = stringify!($name));
315    };
316    ($name:ident = $annotation:expr) => {
317        #[allow(non_camel_case_types)]
318        #[allow(missing_docs)]
319        #[derive(Debug)]
320        pub struct $name(pub $crate::token::Span);
321
322        impl<'a> $crate::parser::Parse<'a> for $name {
323            fn parse(parser: $crate::parser::Parser<'a>) -> $crate::parser::Result<Self> {
324                parser.step(|c| {
325                    if let Some((a, rest)) = c.annotation()? {
326                        if a == $annotation {
327                            return Ok(($name(c.cur_span()), rest));
328                        }
329                    }
330                    Err(c.error(concat!("expected annotation `@", $annotation, "`")))
331                })
332            }
333        }
334
335        impl $crate::parser::Peek for $name {
336            fn peek(cursor: $crate::parser::Cursor<'_>) -> $crate::parser::Result<bool> {
337                Ok(if let Some((a, _rest)) = cursor.annotation()? {
338                    a == $annotation
339                } else {
340                    false
341                })
342            }
343
344            fn display() -> &'static str {
345                concat!("`@", $annotation, "`")
346            }
347        }
348    };
349}
350
351pub mod lexer;
352pub mod parser;
353pub mod token;
354
355#[cfg(feature = "wasm-module")]
356mod encode;
357mod error;
358#[cfg(feature = "wasm-module")]
359mod gensym;
360#[cfg(feature = "wasm-module")]
361mod names;
362pub use self::error::*;
363
364#[cfg(feature = "wasm-module")]
365macro_rules! id {
366    ($($t:tt)*) => ($($t)*)
367}
368
369#[cfg(feature = "wasm-module")]
370id! {
371    mod wast;
372    mod wat;
373    pub use self::wast::*;
374    pub use self::wat::*;
375
376    // Support for core wasm parsing
377    pub mod core;
378
379    // Support for component model parsing
380    #[cfg(feature = "component-model")]
381    pub mod component;
382    #[cfg(not(feature = "component-model"))]
383    #[path = "component_disabled.rs"]
384    pub mod component;
385}
386
387/// Common keyword used to parse WebAssembly text files.
388pub mod kw {
389    custom_keyword!(after);
390    custom_keyword!(alias);
391    custom_keyword!(any);
392    custom_keyword!(anyref);
393    custom_keyword!(arg);
394    custom_keyword!(array);
395    custom_keyword!(arrayref);
396    custom_keyword!(assert_exception);
397    custom_keyword!(assert_exhaustion);
398    custom_keyword!(assert_invalid);
399    custom_keyword!(assert_malformed);
400    custom_keyword!(assert_return);
401    custom_keyword!(assert_trap);
402    custom_keyword!(assert_unlinkable);
403    custom_keyword!(assert_suspension);
404    custom_keyword!(before);
405    custom_keyword!(binary);
406    custom_keyword!(block);
407    custom_keyword!(borrow);
408    custom_keyword!(catch);
409    custom_keyword!(catch_ref);
410    custom_keyword!(catch_all);
411    custom_keyword!(catch_all_ref);
412    custom_keyword!(code);
413    custom_keyword!(cont);
414    custom_keyword!(contref);
415    custom_keyword!(component);
416    custom_keyword!(data);
417    custom_keyword!(declare);
418    custom_keyword!(delegate);
419    custom_keyword!(r#do = "do");
420    custom_keyword!(dtor);
421    custom_keyword!(elem);
422    custom_keyword!(end);
423    custom_keyword!(tag);
424    custom_keyword!(exn);
425    custom_keyword!(exnref);
426    custom_keyword!(export);
427    custom_keyword!(r#extern = "extern");
428    custom_keyword!(externref);
429    custom_keyword!(eq);
430    custom_keyword!(eqref);
431    custom_keyword!(f32);
432    custom_keyword!(f32x4);
433    custom_keyword!(f64);
434    custom_keyword!(f64x2);
435    custom_keyword!(field);
436    custom_keyword!(first);
437    custom_keyword!(func);
438    custom_keyword!(funcref);
439    custom_keyword!(get);
440    custom_keyword!(global);
441    custom_keyword!(i16);
442    custom_keyword!(i16x8);
443    custom_keyword!(i31);
444    custom_keyword!(i31ref);
445    custom_keyword!(i32);
446    custom_keyword!(i32x4);
447    custom_keyword!(i64);
448    custom_keyword!(i64x2);
449    custom_keyword!(i8);
450    custom_keyword!(i8x16);
451    custom_keyword!(import);
452    custom_keyword!(instance);
453    custom_keyword!(instantiate);
454    custom_keyword!(interface);
455    custom_keyword!(invoke);
456    custom_keyword!(item);
457    custom_keyword!(last);
458    custom_keyword!(local);
459    custom_keyword!(memory);
460    custom_keyword!(module);
461    custom_keyword!(modulecode);
462    custom_keyword!(nan_arithmetic = "nan:arithmetic");
463    custom_keyword!(nan_canonical = "nan:canonical");
464    custom_keyword!(nocont);
465    custom_keyword!(nofunc);
466    custom_keyword!(noextern);
467    custom_keyword!(noexn);
468    custom_keyword!(none);
469    custom_keyword!(null);
470    custom_keyword!(nullcontref);
471    custom_keyword!(nullfuncref);
472    custom_keyword!(nullexternref);
473    custom_keyword!(nullexnref);
474    custom_keyword!(nullref);
475    custom_keyword!(offset);
476    custom_keyword!(on);
477    custom_keyword!(outer);
478    custom_keyword!(own);
479    custom_keyword!(pagesize);
480    custom_keyword!(param);
481    custom_keyword!(parent);
482    custom_keyword!(passive);
483    custom_keyword!(quote);
484    custom_keyword!(r#else = "else");
485    custom_keyword!(r#if = "if");
486    custom_keyword!(r#loop = "loop");
487    custom_keyword!(r#mut = "mut");
488    custom_keyword!(r#type = "type");
489    custom_keyword!(r#ref = "ref");
490    custom_keyword!(ref_func = "ref.func");
491    custom_keyword!(ref_null = "ref.null");
492    custom_keyword!(register);
493    custom_keyword!(rec);
494    custom_keyword!(acq_rel);
495    custom_keyword!(rep);
496    custom_keyword!(resource);
497    custom_keyword!(resource_new = "resource.new");
498    custom_keyword!(resource_drop = "resource.drop");
499    custom_keyword!(resource_rep = "resource.rep");
500    custom_keyword!(result);
501    custom_keyword!(seq_cst);
502    custom_keyword!(shared);
503    custom_keyword!(start);
504    custom_keyword!(sub);
505    custom_keyword!(switch);
506    custom_keyword!(r#final = "final");
507    custom_keyword!(table);
508    custom_keyword!(then);
509    custom_keyword!(r#try = "try");
510    custom_keyword!(v128);
511    custom_keyword!(value);
512    custom_keyword!(s8);
513    custom_keyword!(s16);
514    custom_keyword!(s32);
515    custom_keyword!(s64);
516    custom_keyword!(u8);
517    custom_keyword!(u16);
518    custom_keyword!(u32);
519    custom_keyword!(u64);
520    custom_keyword!(char);
521    custom_keyword!(case);
522    custom_keyword!(refines);
523    custom_keyword!(record);
524    custom_keyword!(string);
525    custom_keyword!(bool_ = "bool");
526    custom_keyword!(float32);
527    custom_keyword!(float64);
528    custom_keyword!(variant);
529    custom_keyword!(flags);
530    custom_keyword!(option);
531    custom_keyword!(tuple);
532    custom_keyword!(list);
533    custom_keyword!(error);
534    custom_keyword!(canon);
535    custom_keyword!(lift);
536    custom_keyword!(lower);
537    custom_keyword!(enum_ = "enum");
538    custom_keyword!(string_utf8 = "string-encoding=utf8");
539    custom_keyword!(string_utf16 = "string-encoding=utf16");
540    custom_keyword!(string_latin1_utf16 = "string-encoding=latin1+utf16");
541    custom_keyword!(r#struct = "struct");
542    custom_keyword!(structref);
543    custom_keyword!(realloc);
544    custom_keyword!(post_return = "post-return");
545    custom_keyword!(with);
546    custom_keyword!(core);
547    custom_keyword!(true_ = "true");
548    custom_keyword!(false_ = "false");
549    custom_keyword!(language);
550    custom_keyword!(sdk);
551    custom_keyword!(processed_by = "processed-by");
552    custom_keyword!(mem_info = "mem-info");
553    custom_keyword!(needed);
554    custom_keyword!(export_info = "export-info");
555    custom_keyword!(import_info = "import-info");
556    custom_keyword!(thread);
557    custom_keyword!(thread_spawn = "thread.spawn");
558    custom_keyword!(thread_available_parallelism = "thread.available_parallelism");
559    custom_keyword!(task_backpressure = "task.backpressure");
560    custom_keyword!(task_return = "task.return");
561    custom_keyword!(task_wait = "task.wait");
562    custom_keyword!(task_poll = "task.poll");
563    custom_keyword!(task_yield = "task.yield");
564    custom_keyword!(subtask_drop = "subtask.drop");
565    custom_keyword!(stream_new = "stream.new");
566    custom_keyword!(stream_read = "stream.read");
567    custom_keyword!(stream_write = "stream.write");
568    custom_keyword!(stream_cancel_read = "stream.cancel-read");
569    custom_keyword!(stream_cancel_write = "stream.cancel-write");
570    custom_keyword!(stream_close_readable = "stream.close-readable");
571    custom_keyword!(stream_close_writable = "stream.close-writable");
572    custom_keyword!(future_new = "future.new");
573    custom_keyword!(future_read = "future.read");
574    custom_keyword!(future_write = "future.write");
575    custom_keyword!(future_cancel_read = "future.cancel-read");
576    custom_keyword!(future_cancel_write = "future.cancel-write");
577    custom_keyword!(future_close_readable = "future.close-readable");
578    custom_keyword!(future_close_writable = "future.close-writable");
579    custom_keyword!(error_context_new = "error-context.new");
580    custom_keyword!(error_context_debug_message = "error-context.debug-message");
581    custom_keyword!(error_context_drop = "error-context.drop");
582    custom_keyword!(wait);
583    custom_keyword!(definition);
584    custom_keyword!(r#async = "async");
585    custom_keyword!(callback);
586    custom_keyword!(stream);
587    custom_keyword!(future);
588}
589
590/// Common annotations used to parse WebAssembly text files.
591pub mod annotation {
592    annotation!(custom);
593    annotation!(name);
594    annotation!(producers);
595    annotation!(dylink_0 = "dylink.0");
596    annotation!(metadata_code_branch_hint = "metadata.code.branch_hint");
597}