wain_syntax_text/
ast.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fmt;
4
5// TODO: Remove duplication between text format AST and binary format AST.
6// For example, wat::ValType and wasm::TableType has the same structure. So we can use
7// wasm::TableType directly in parsing WAT.
8
9type Indices<'s> = HashMap<&'s str, u32>;
10
11// Root of the tree
12#[cfg_attr(test, derive(Debug))]
13pub struct Parsed<'s> {
14    pub module: Module<'s>,
15    pub type_indices: Indices<'s>,
16    pub func_indices: Indices<'s>,
17    pub table_indices: Indices<'s>,
18    pub mem_indices: Indices<'s>,
19    pub global_indices: Indices<'s>,
20}
21
22// Note: Since crate for syntax tree data structure is separated, all fields of AST node structs need
23// to be public. Or we need a factory function like Module::new() for each struct.
24
25// https://webassembly.github.io/spec/core/text/modules.html#text-module
26#[cfg_attr(test, derive(Debug))]
27pub struct Module<'s> {
28    pub start: usize,
29    pub id: Option<&'s str>,
30    pub types: Vec<TypeDef<'s>>,
31    pub exports: Vec<Export<'s>>,
32    pub funcs: Vec<Func<'s>>,
33    pub elems: Vec<Elem<'s>>,
34    pub tables: Vec<Table<'s>>,
35    pub data: Vec<Data<'s>>,
36    pub memories: Vec<Memory<'s>>,
37    pub globals: Vec<Global<'s>>,
38    pub entrypoint: Option<Start<'s>>,
39    pub implicit_type_uses: Vec<u32>,
40}
41
42// https://webassembly.github.io/spec/core/text/modules.html#text-typedef
43#[cfg_attr(test, derive(Debug))]
44pub struct TypeDef<'s> {
45    pub start: usize,
46    pub id: Option<&'s str>,
47    pub ty: FuncType<'s>,
48}
49
50// https://webassembly.github.io/spec/core/text/types.html#text-functype
51#[derive(Clone)]
52#[cfg_attr(test, derive(Debug))]
53pub struct FuncType<'s> {
54    pub start: usize,
55    pub params: Vec<Param<'s>>,
56    pub results: Vec<FuncResult>,
57}
58
59// https://webassembly.github.io/spec/core/text/types.html#text-param
60#[derive(Clone)]
61#[cfg_attr(test, derive(Debug))]
62pub struct Param<'s> {
63    pub start: usize,
64    pub id: Option<&'s str>,
65    pub ty: ValType,
66}
67
68// https://webassembly.github.io/spec/core/text/types.html#text-result
69#[derive(Clone)]
70#[cfg_attr(test, derive(Debug))]
71pub struct FuncResult {
72    pub start: usize,
73    pub ty: ValType,
74}
75
76// https://webassembly.github.io/spec/core/text/types.html#text-valtype
77#[derive(PartialEq, Clone, Copy)]
78#[cfg_attr(test, derive(Debug))]
79pub enum ValType {
80    I32,
81    I64,
82    F32,
83    F64,
84}
85
86impl fmt::Display for ValType {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.write_str(match self {
89            ValType::I32 => "i32",
90            ValType::I64 => "i64",
91            ValType::F32 => "f32",
92            ValType::F64 => "f64",
93        })
94    }
95}
96
97// https://webassembly.github.io/spec/core/text/modules.html#text-import
98#[cfg_attr(test, derive(Debug))]
99pub struct Import<'s> {
100    pub mod_name: Name<'s>,
101    pub name: Name<'s>,
102}
103
104// https://webassembly.github.io/spec/core/text/values.html#text-name
105//
106// Use Cow<'s, str> since it is String on text format and it is &str on binary format.
107// In text format, special characters in string literal are escaped. Unescaped string must
108// be allocated in heap. In binary format, it is directly encoded as bytes so borrowing the
109// part of source is enough.
110#[cfg_attr(test, derive(Debug))]
111pub struct Name<'s>(pub Cow<'s, str>);
112
113// Type index has two flavors. One is specified by user explicitly in u32 index or identifier name.
114// Another is inserted implicitly and automatically by interpreter when typeuse is omitted.
115// For the implicit typeuse, we use a separate index space and resolve the indices after.
116#[cfg_attr(test, derive(Debug, PartialEq))]
117pub enum TypeIndex<'s> {
118    Explicit(Index<'s>),
119    Implicit(u32),
120}
121
122// https://webassembly.github.io/spec/core/text/modules.html#type-uses
123#[cfg_attr(test, derive(Debug))]
124pub struct TypeUse<'s> {
125    pub start: usize,
126    pub idx: TypeIndex<'s>,
127    pub params: Vec<Param<'s>>,
128    pub results: Vec<FuncResult>,
129}
130
131// https://webassembly.github.io/spec/core/text/modules.html#type-uses
132pub struct ImplicitTypeUse<'s> {
133    pub start: usize,
134    pub params: Vec<Param<'s>>,
135    pub results: Vec<FuncResult>,
136}
137
138// https://webassembly.github.io/spec/core/text/modules.html#indices
139#[cfg_attr(test, derive(Debug, PartialEq))]
140pub enum Index<'s> {
141    Num(u32),
142    Ident(&'s str),
143}
144impl<'s> fmt::Display for Index<'s> {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self {
147            Index::Num(i) => write!(f, "{}", i),
148            Index::Ident(i) => write!(f, "{}", i),
149        }
150    }
151}
152
153// https://webassembly.github.io/spec/core/text/types.html#text-tabletype
154// Note: elemtype is currently fixed to 'funcref'
155#[cfg_attr(test, derive(Debug))]
156pub struct TableType {
157    pub limit: Limits,
158}
159
160// https://webassembly.github.io/spec/core/text/types.html#text-limits
161#[cfg_attr(test, derive(Debug))]
162pub enum Limits {
163    Range { min: u32, max: u32 },
164    From { min: u32 },
165}
166
167// https://webassembly.github.io/spec/core/text/types.html#text-memtype
168#[cfg_attr(test, derive(Debug))]
169pub struct MemType {
170    pub limit: Limits,
171}
172
173// https://webassembly.github.io/spec/core/text/types.html#text-globaltype
174#[cfg_attr(test, derive(Debug))]
175pub struct GlobalType {
176    pub mutable: bool,
177    pub ty: ValType,
178}
179
180// https://webassembly.github.io/spec/core/text/modules.html#text-export
181#[cfg_attr(test, derive(Debug))]
182pub struct Export<'s> {
183    pub start: usize,
184    pub name: Name<'s>,
185    pub kind: ExportKind,
186    pub idx: Index<'s>,
187}
188
189// https://webassembly.github.io/spec/core/text/modules.html#text-exportdesc
190#[cfg_attr(test, derive(Debug))]
191pub enum ExportKind {
192    Func,
193    Table,
194    Memory,
195    Global,
196}
197
198// https://webassembly.github.io/spec/core/text/modules.html#text-func
199#[cfg_attr(test, derive(Debug))]
200pub enum FuncKind<'s> {
201    Import(Import<'s>),
202    Body {
203        locals: Vec<Local<'s>>,
204        body: Vec<Instruction<'s>>,
205    },
206}
207#[cfg_attr(test, derive(Debug))]
208pub struct Func<'s> {
209    pub start: usize,
210    pub id: Option<&'s str>,
211    pub ty: TypeUse<'s>,
212    pub kind: FuncKind<'s>,
213}
214
215// https://webassembly.github.io/spec/core/text/modules.html#text-local
216#[cfg_attr(test, derive(Debug))]
217pub struct Local<'s> {
218    pub start: usize,
219    pub id: Option<&'s str>,
220    pub ty: ValType,
221}
222
223// https://webassembly.github.io/spec/core/text/instructions.html#instructions
224#[cfg_attr(test, derive(Debug))]
225pub struct Instruction<'s> {
226    pub start: usize,
227    pub kind: InsnKind<'s>,
228}
229
230// https://webassembly.github.io/spec/core/text/instructions.html#text-memarg
231#[cfg_attr(test, derive(Debug))]
232pub struct Mem {
233    pub align: u32,
234    pub offset: u32,
235}
236
237#[cfg_attr(test, derive(Debug))]
238pub enum InsnKind<'s> {
239    // Control instructions
240    // https://webassembly.github.io/spec/core/text/instructions.html#control-instructions
241    Block {
242        label: Option<&'s str>,
243        ty: Option<ValType>,
244        body: Vec<Instruction<'s>>,
245        id: Option<&'s str>,
246    },
247    Loop {
248        label: Option<&'s str>,
249        ty: Option<ValType>,
250        body: Vec<Instruction<'s>>,
251        id: Option<&'s str>,
252    },
253    If {
254        label: Option<&'s str>,
255        ty: Option<ValType>,
256        then_body: Vec<Instruction<'s>>,
257        else_id: Option<&'s str>,
258        else_body: Vec<Instruction<'s>>,
259        end_id: Option<&'s str>,
260    },
261    Unreachable,
262    Nop,
263    Br(Index<'s>),
264    BrIf(Index<'s>),
265    BrTable {
266        labels: Vec<Index<'s>>,
267        default_label: Index<'s>,
268    },
269    Return,
270    Call(Index<'s>),
271    CallIndirect(TypeUse<'s>),
272    // Parametric instructions
273    // https://webassembly.github.io/spec/core/text/instructions.html#parametric-instructions
274    Drop,
275    Select,
276    // Variable instructions
277    // https://webassembly.github.io/spec/core/text/instructions.html#variable-instructions
278    LocalGet(Index<'s>),
279    LocalSet(Index<'s>),
280    LocalTee(Index<'s>),
281    GlobalGet(Index<'s>),
282    GlobalSet(Index<'s>),
283    // Memory instructions
284    // https://webassembly.github.io/spec/core/text/instructions.html#memory-instructions
285    I32Load(Mem),
286    I64Load(Mem),
287    F32Load(Mem),
288    F64Load(Mem),
289    I32Load8S(Mem),
290    I32Load8U(Mem),
291    I32Load16S(Mem),
292    I32Load16U(Mem),
293    I64Load8S(Mem),
294    I64Load8U(Mem),
295    I64Load16S(Mem),
296    I64Load16U(Mem),
297    I64Load32S(Mem),
298    I64Load32U(Mem),
299    I32Store(Mem),
300    I64Store(Mem),
301    F32Store(Mem),
302    F64Store(Mem),
303    I32Store8(Mem),
304    I32Store16(Mem),
305    I64Store8(Mem),
306    I64Store16(Mem),
307    I64Store32(Mem),
308    MemorySize,
309    MemoryGrow,
310    // Numeric instructions
311    // https://webassembly.github.io/spec/core/text/instructions.html#numeric-instructions
312    // Constants
313    I32Const(i32),
314    I64Const(i64),
315    F32Const(f32),
316    F64Const(f64),
317    // i32 operations
318    I32Clz,
319    I32Ctz,
320    I32Popcnt,
321    I32Add,
322    I32Sub,
323    I32Mul,
324    I32DivS,
325    I32DivU,
326    I32RemS,
327    I32RemU,
328    I32And,
329    I32Or,
330    I32Xor,
331    I32Shl,
332    I32ShrS,
333    I32ShrU,
334    I32Rotl,
335    I32Rotr,
336    // i64 operations
337    I64Clz,
338    I64Ctz,
339    I64Popcnt,
340    I64Add,
341    I64Sub,
342    I64Mul,
343    I64DivS,
344    I64DivU,
345    I64RemS,
346    I64RemU,
347    I64And,
348    I64Or,
349    I64Xor,
350    I64Shl,
351    I64ShrS,
352    I64ShrU,
353    I64Rotl,
354    I64Rotr,
355    // f32 operations
356    F32Abs,
357    F32Neg,
358    F32Ceil,
359    F32Floor,
360    F32Trunc,
361    F32Nearest,
362    F32Sqrt,
363    F32Add,
364    F32Sub,
365    F32Mul,
366    F32Div,
367    F32Min,
368    F32Max,
369    F32Copysign,
370    // f64 operations
371    F64Abs,
372    F64Neg,
373    F64Ceil,
374    F64Floor,
375    F64Trunc,
376    F64Nearest,
377    F64Sqrt,
378    F64Add,
379    F64Sub,
380    F64Mul,
381    F64Div,
382    F64Min,
383    F64Max,
384    F64Copysign,
385    // i32 comparison
386    I32Eqz,
387    I32Eq,
388    I32Ne,
389    I32LtS,
390    I32LtU,
391    I32GtS,
392    I32GtU,
393    I32LeS,
394    I32LeU,
395    I32GeS,
396    I32GeU,
397    // i64 comparison
398    I64Eqz,
399    I64Eq,
400    I64Ne,
401    I64LtS,
402    I64LtU,
403    I64GtS,
404    I64GtU,
405    I64LeS,
406    I64LeU,
407    I64GeS,
408    I64GeU,
409    // f32 comparison
410    F32Eq,
411    F32Ne,
412    F32Lt,
413    F32Gt,
414    F32Le,
415    F32Ge,
416    // f64 comparison
417    F64Eq,
418    F64Ne,
419    F64Lt,
420    F64Gt,
421    F64Le,
422    F64Ge,
423    // Conversion
424    I32WrapI64,
425    I32TruncF32S,
426    I32TruncF32U,
427    I32TruncF64S,
428    I32TruncF64U,
429    I64ExtendI32S,
430    I64ExtendI32U,
431    I64TruncF32S,
432    I64TruncF32U,
433    I64TruncF64S,
434    I64TruncF64U,
435    F32ConvertI32S,
436    F32ConvertI32U,
437    F32ConvertI64S,
438    F32ConvertI64U,
439    F32DemoteF64,
440    F64ConvertI32S,
441    F64ConvertI32U,
442    F64ConvertI64S,
443    F64ConvertI64U,
444    F64PromoteF32,
445    I32ReinterpretF32,
446    I64ReinterpretF64,
447    F32ReinterpretI32,
448    F64ReinterpretI64,
449    // Sign extension
450    I32Extend8S,
451    I32Extend16S,
452    I64Extend8S,
453    I64Extend16S,
454    I64Extend32S,
455}
456
457impl<'s> InsnKind<'s> {
458    pub fn is_block(&self) -> bool {
459        use InsnKind::*;
460        matches!(self, Block { .. } | Loop { .. } | If { .. })
461    }
462}
463
464// https://webassembly.github.io/spec/core/text/modules.html#element-segments
465#[cfg_attr(test, derive(Debug))]
466pub struct Elem<'s> {
467    pub start: usize,
468    pub idx: Index<'s>,
469    pub offset: Vec<Instruction<'s>>,
470    pub init: Vec<Index<'s>>,
471}
472
473// https://webassembly.github.io/spec/core/text/modules.html#tables
474#[cfg_attr(test, derive(Debug))]
475pub struct Table<'s> {
476    pub start: usize,
477    pub id: Option<&'s str>,
478    pub ty: TableType,
479    pub import: Option<Import<'s>>,
480}
481
482// https://webassembly.github.io/spec/core/text/modules.html#text-data
483#[cfg_attr(test, derive(Debug))]
484pub struct Data<'s> {
485    pub start: usize,
486    pub idx: Index<'s>,
487    pub offset: Vec<Instruction<'s>>,
488    pub data: Cow<'s, [u8]>,
489}
490
491// https://webassembly.github.io/spec/core/text/modules.html#memories
492#[cfg_attr(test, derive(Debug))]
493pub struct Memory<'s> {
494    pub start: usize,
495    pub id: Option<&'s str>,
496    pub ty: MemType,
497    pub import: Option<Import<'s>>,
498}
499
500// https://webassembly.github.io/spec/core/text/modules.html#globals
501#[cfg_attr(test, derive(Debug))]
502pub enum GlobalKind<'s> {
503    Import(Import<'s>),
504    Init(Vec<Instruction<'s>>),
505}
506#[cfg_attr(test, derive(Debug))]
507pub struct Global<'s> {
508    pub start: usize,
509    pub id: Option<&'s str>,
510    pub ty: GlobalType,
511    pub kind: GlobalKind<'s>,
512}
513
514// https://webassembly.github.io/spec/core/text/modules.html#text-start
515#[cfg_attr(test, derive(Debug))]
516pub struct Start<'s> {
517    pub start: usize,
518    pub idx: Index<'s>,
519}
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524
525    #[test]
526    fn insn_is_block() {
527        let insn = InsnKind::Block {
528            label: None,
529            ty: None,
530            body: vec![],
531            id: None,
532        };
533        assert!(insn.is_block());
534        assert!(!InsnKind::Nop.is_block());
535    }
536}