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_hw_concurrency = "thread.hw_concurrency");
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}
598