Skip to main content

tarnik_ast/
lib.rs

1use std::collections::HashMap;
2use std::fmt::{self};
3use std::str::FromStr;
4
5pub use indexmap::IndexMap;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum Nullable {
9    True,
10    False,
11}
12
13impl fmt::Display for Nullable {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            Nullable::True => write!(f, "null"),
17            Nullable::False => write!(f, ""),
18        }
19    }
20}
21
22#[derive(Debug, Clone, Eq, PartialEq)]
23pub struct StructField {
24    pub name: Option<String>,
25    pub ty: WasmType,
26    pub mutable: bool,
27}
28
29impl fmt::Display for StructField {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        let name = self.name.clone().unwrap_or_default();
32        let ty = if self.mutable {
33            format!("(mut {})", self.ty)
34        } else {
35            format!("{}", self.ty)
36        };
37        writeln!(f, "(field {name} {ty}")
38    }
39}
40
41#[derive(Debug, Clone, Eq, PartialEq)]
42pub struct Signature {
43    pub params: Vec<WasmType>,
44    pub result: Option<Box<WasmType>>,
45}
46
47#[derive(Debug, Clone, Eq, PartialEq)]
48pub enum WasmType {
49    I32,
50    I64,
51    F32,
52    F64,
53    I8,
54    I31Ref,
55    Anyref,
56    Ref(String, Nullable),
57    Array {
58        mutable: bool,
59        ty: Box<WasmType>,
60    },
61    Struct(Vec<StructField>),
62    Func(Box<Signature>),
63    Tag {
64        name: String,
65        signature: Box<Signature>,
66    },
67}
68
69impl WasmType {
70    pub fn is_numeric(&self) -> bool {
71        use WasmType::*;
72        matches!(self, I32 | I64 | F32 | I8 | I31Ref)
73    }
74
75    pub fn compatible_numeric_types(left: &Self, right: &Self) -> bool {
76        use WasmType::*;
77        matches!(
78            (left, right),
79            (I32, I32)
80                | (I32, I64)
81                | (I32, I8)
82                | (I32, I31Ref)
83                | (I64, I32)
84                | (I64, I64)
85                | (I64, I8)
86                | (I64, I31Ref)
87                | (I8, I32)
88                | (I8, I64)
89                | (I8, I8)
90                | (I8, I31Ref)
91                | (I31Ref, I32)
92                | (I31Ref, I64)
93                | (I31Ref, I8)
94                | (I31Ref, I31Ref)
95                | (F32, F32)
96                | (F32, F64)
97                | (F64, F32)
98                | (F64, F64)
99        )
100    }
101
102    pub fn broader_numeric_type(left: &Self, right: &Self) -> Self {
103        use WasmType::*;
104        match (left, right) {
105            (I32, I32) => I32,
106            (I32, I64) => I64,
107            (I32, I8) => I32,
108            (I32, I31Ref) => I32,
109            (I64, I32) => I64,
110            (I64, I64) => I64,
111            (I64, I8) => I64,
112            (I64, I31Ref) => I64,
113            (I8, I32) => I32,
114            (I8, I64) => I64,
115            (I8, I8) => I8,
116            (I8, I31Ref) => I31Ref,
117            (I31Ref, I32) => I32,
118            (I31Ref, I64) => I64,
119            (I31Ref, I8) => I31Ref,
120            (I31Ref, I31Ref) => I31Ref,
121            (F32, F32) => F32,
122            (F32, F64) => F64,
123            (F64, F32) => F64,
124            (F64, F64) => F64,
125            _ => panic!("Both types need to be numeric. Left: {left:?}, Right: {right:?}."),
126        }
127    }
128
129    pub fn convert_to_instruction(&self, to_type: &Self) -> Vec<WatInstruction> {
130        use WasmType::*;
131        match (self, to_type) {
132            (I32, I32) => vec![],
133            (I32, I64) => vec![WatInstruction::I64ExtendI32S],
134            (I32, I31Ref) => vec![WatInstruction::RefI31],
135            (I64, I32) => vec![WatInstruction::I32WrapI64],
136            (I64, I64) => vec![],
137            (I64, I31Ref) => vec![WatInstruction::I32WrapI64, WatInstruction::RefI31],
138            (I31Ref, I32) => vec![WatInstruction::I31GetS],
139            (I31Ref, I64) => vec![WatInstruction::I31GetS, WatInstruction::I64ExtendI32S],
140            (I31Ref, I31Ref) => vec![],
141            (F32, F32) => vec![],
142            (F32, F64) => vec![WatInstruction::F64PromoteF32],
143            (F64, F32) => vec![WatInstruction::F32DemoteF64],
144            (F64, F64) => vec![],
145            _ => panic!("Can't convert {self:?} to {to_type:?}"),
146        }
147    }
148}
149
150impl fmt::Display for WasmType {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        match self {
153            WasmType::I32 => write!(f, "i32"),
154            WasmType::I64 => write!(f, "i64"),
155            WasmType::F32 => write!(f, "f32"),
156            WasmType::F64 => write!(f, "f64"),
157            WasmType::I8 => write!(f, "i8"),
158            WasmType::Anyref => write!(f, "anyref"),
159            WasmType::I31Ref => write!(f, "i31ref"),
160            WasmType::Ref(name, nullable) => write!(f, "(ref {nullable} {name})"),
161            WasmType::Array { mutable, ty } => {
162                let m = if *mutable {
163                    format!("(mut {ty})")
164                } else {
165                    ty.to_string()
166                };
167                write!(f, "(array {m})")
168            }
169            WasmType::Struct(fields) => {
170                write!(f, "(struct")?;
171                for field in fields {
172                    write!(f, "  {field}")?;
173                }
174                write!(f, ")")
175            }
176            WasmType::Func(signature) => {
177                write!(f, "(func")?;
178                for param in &signature.params {
179                    write!(f, " (param {param})")?;
180                }
181                if let Some(result) = &signature.result {
182                    write!(f, " (result {result})")?;
183                }
184                write!(f, ")")
185            }
186            WasmType::Tag { name, signature } => {
187                write!(f, "(tag {name}")?;
188                for param in &signature.params {
189                    write!(f, " (param {param})")?;
190                }
191                if let Some(result) = &signature.result {
192                    write!(f, " (result {result})")?;
193                }
194                write!(f, ")")
195            }
196        }
197    }
198}
199
200#[derive(Debug, Clone)]
201pub struct WasmError;
202
203impl FromStr for WasmType {
204    // TODO:implement error handling
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        let str = match s {
207            "i32" => Self::I32,
208            "i64" => Self::I64,
209            "f32" => Self::F32,
210            "f64" => Self::F64,
211            "i8" => Self::I8,
212            "i31ref" => Self::I31Ref,
213            "anyref" => Self::Anyref,
214            // this should not go here, as this crate is supposed to not
215            // be tied to a certain implementation, but for now I'm leaving it here
216            // TODO: fix this
217            "bool" => Self::I32,
218            _ => {
219                // for now just handle custom types
220                Self::Ref(format!("${s}"), Nullable::False)
221            }
222        };
223
224        Ok(str)
225    }
226
227    type Err = WasmError;
228}
229
230#[derive(Debug, Clone)]
231pub enum WatInstruction {
232    Nop,
233    Local {
234        name: String,
235        r#type: WasmType,
236    },
237    GlobalGet(String),
238    LocalGet(String),
239    LocalSet(String),
240    Call(String),
241
242    I32Const(i32),
243    I64Const(i64),
244    F32Const(f32),
245    F64Const(f64),
246
247    I32Eqz,
248    I64Eqz,
249    F32Eqz,
250    F64Eqz,
251
252    I32Eq,
253    I64Eq,
254    F32Eq,
255    F64Eq,
256
257    I32GeS,
258    I64ExtendI32S,
259    I32WrapI64,
260    I31GetS,
261    F64PromoteF32,
262    F32DemoteF64,
263
264    StructNew(String),
265    StructGet(String, String),
266    StructSet(String, String),
267    ArrayNew(String),
268    ArrayNewFixed(String, u16),
269    ArrayLen,
270    ArrayGet(String),
271    ArrayGetU(String),
272    ArraySet(String),
273    RefNull(WasmType),
274    Ref(String),
275    RefFunc(String),
276    Type(String),
277    Return,
278    ReturnCall(String),
279    Block {
280        label: String,
281        instructions: Vec<WatInstruction>,
282    },
283    Loop {
284        label: String,
285        instructions: Vec<WatInstruction>,
286    },
287    If {
288        then: Vec<WatInstruction>,
289        r#else: Option<Vec<WatInstruction>>,
290    },
291    BrIf(String),
292    Br(String),
293    Empty,
294    Log,
295    Identifier(String),
296    Drop,
297    LocalTee(String),
298    RefI31,
299    Throw(String),
300    Try {
301        try_block: Box<WatInstruction>,
302        catches: Vec<Box<WatInstruction>>,
303        catch_all: Option<Box<WatInstruction>>,
304    },
305    Catch(String, Box<WatInstruction>),
306    CatchAll(Box<WatInstruction>),
307    I32Add,
308    I64Add,
309    F32Add,
310    F64Add,
311}
312
313impl WatInstruction {
314    pub fn local(name: impl Into<String>, r#type: WasmType) -> Box<Self> {
315        Box::new(Self::Local {
316            name: name.into(),
317            r#type,
318        })
319    }
320
321    pub fn global_get(name: impl Into<String>) -> Self {
322        Self::GlobalGet(name.into())
323    }
324
325    pub fn local_get(name: impl Into<String>) -> Self {
326        Self::LocalGet(name.into())
327    }
328
329    pub fn local_set(name: impl Into<String>) -> Self {
330        Self::LocalSet(name.into())
331    }
332
333    pub fn local_tee(name: impl Into<String>) -> Self {
334        Self::LocalTee(name.into())
335    }
336
337    pub fn call(name: impl Into<String>) -> Self {
338        Self::Call(name.into())
339    }
340
341    pub fn i32_const(value: i32) -> Self {
342        Self::I32Const(value)
343    }
344
345    pub fn f64_const(value: f64) -> Box<Self> {
346        Box::new(Self::F64Const(value))
347    }
348
349    pub fn struct_new(name: impl Into<String>) -> Self {
350        Self::StructNew(name.into())
351    }
352
353    pub fn struct_get(name: impl Into<String>, field_name: impl Into<String>) -> Self {
354        Self::StructGet(name.into(), field_name.into())
355    }
356
357    pub fn struct_set(name: impl Into<String>, field_name: impl Into<String>) -> Self {
358        Self::StructSet(name.into(), field_name.into())
359    }
360
361    pub fn array_new(
362        name: impl Into<String>,
363        _init: Box<WatInstruction>,
364        _length: Box<WatInstruction>,
365    ) -> Box<Self> {
366        Box::new(Self::ArrayNew(name.into()))
367    }
368
369    pub fn array_get(name: impl Into<String>) -> Self {
370        Self::ArrayGet(name.into())
371    }
372
373    pub fn array_get_u(name: impl Into<String>) -> Self {
374        Self::ArrayGetU(name.into())
375    }
376
377    pub fn array_set(name: impl Into<String>) -> Self {
378        Self::ArraySet(name.into())
379    }
380
381    pub fn ref_null(r#type: WasmType) -> Box<Self> {
382        Box::new(Self::RefNull(r#type))
383    }
384
385    pub fn ref_func(name: impl Into<String>) -> Box<Self> {
386        Box::new(Self::RefFunc(name.into()))
387    }
388
389    pub fn type_(name: impl Into<String>) -> Box<Self> {
390        Box::new(Self::Type(name.into()))
391    }
392
393    pub fn r#return() -> Box<Self> {
394        Box::new(Self::Return)
395    }
396
397    pub fn return_call(name: impl Into<String>) -> Box<Self> {
398        Box::new(Self::ReturnCall(name.into()))
399    }
400
401    pub fn block(label: impl Into<String>, instructions: Vec<WatInstruction>) -> Self {
402        Self::Block {
403            label: label.into(),
404            instructions,
405        }
406    }
407
408    pub fn r#loop(label: impl Into<String>, instructions: Vec<WatInstruction>) -> Self {
409        Self::Loop {
410            label: label.into(),
411            instructions,
412        }
413    }
414
415    pub fn r#if(then: Vec<WatInstruction>, r#else: Option<Vec<WatInstruction>>) -> Self {
416        Self::If { then, r#else }
417    }
418
419    pub fn br_if(label: impl Into<String>) -> Self {
420        Self::BrIf(label.into())
421    }
422
423    pub fn br(label: impl Into<String>) -> Self {
424        Self::Br(label.into())
425    }
426
427    pub fn empty() -> Box<Self> {
428        Box::new(Self::Empty)
429    }
430
431    pub fn drop() -> Box<Self> {
432        Box::new(Self::Drop)
433    }
434
435    pub fn i32_eqz() -> Box<Self> {
436        Box::new(Self::I32Eqz)
437    }
438
439    pub fn ref_i31() -> Self {
440        Self::RefI31
441    }
442
443    pub fn throw(label: impl Into<String>) -> Self {
444        Self::Throw(label.into())
445    }
446
447    pub fn r#type(name: impl Into<String>) -> Box<Self> {
448        Box::new(Self::Type(name.into()))
449    }
450
451    pub fn r#try(
452        try_block: Box<Self>,
453        catches: Vec<Box<Self>>,
454        catch_all: Option<Box<Self>>,
455    ) -> Box<Self> {
456        Box::new(Self::Try {
457            try_block,
458            catches,
459            catch_all,
460        })
461    }
462
463    pub fn catch(label: impl Into<String>, instr: Box<Self>) -> Box<Self> {
464        Box::new(Self::Catch(label.into(), instr))
465    }
466
467    pub fn is_return(&self) -> bool {
468        matches!(self, Self::Return)
469    }
470
471    pub fn is_call(&self) -> bool {
472        matches!(self, Self::Call { .. })
473    }
474}
475
476impl fmt::Display for WatInstruction {
477    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478        match self {
479            WatInstruction::I32Add => write!(f, "(i32.add)"),
480            WatInstruction::I64Add => write!(f, "(i64.add)"),
481            WatInstruction::F32Add => write!(f, "(f32.add)"),
482            WatInstruction::F64Add => write!(f, "(f64.add)"),
483
484            WatInstruction::Nop => Ok(()),
485            WatInstruction::Local { name, r#type } => write!(f, "(local {} {})", name, r#type),
486            WatInstruction::GlobalGet(name) => write!(f, "(global.get {})", name),
487            WatInstruction::LocalGet(name) => write!(f, "(local.get {})", name),
488            WatInstruction::LocalSet(name) => write!(f, "(local.set {})", name),
489            WatInstruction::Call(name) => writeln!(f, "(call {})", name),
490
491            WatInstruction::I32Const(value) => write!(f, "(i32.const {})", value),
492            WatInstruction::I64Const(value) => write!(f, "(i64.const {})", value),
493            WatInstruction::F32Const(value) => write!(f, "(f32.const {})", value),
494            WatInstruction::F64Const(value) => write!(f, "(f64.const {})", value),
495
496            WatInstruction::I32GeS => writeln!(f, "(i32.ge_s)"),
497
498            WatInstruction::StructNew(name) => write!(f, "(struct.new {})", name),
499            WatInstruction::StructGet(name, field) => write!(f, "(struct.get {name} {field})"),
500            WatInstruction::StructSet(name, field) => write!(f, "(struct.set {name} {field})"),
501            WatInstruction::ArrayNew(name) => {
502                write!(f, "(array.new {name})")
503            }
504            WatInstruction::ArrayLen => write!(f, "(array.len)"),
505            WatInstruction::ArrayGet(ty) => write!(f, "(array.get {ty})"),
506            WatInstruction::ArrayGetU(ty) => write!(f, "(array.get_u {ty})"),
507            WatInstruction::ArraySet(ty) => write!(f, "(array.set {ty})"),
508            WatInstruction::ArrayNewFixed(typeidx, n) => {
509                write!(f, "(array.new_fixed {typeidx} {n})")
510            }
511            WatInstruction::RefNull(r#type) => write!(f, "(ref.null {})", r#type),
512            WatInstruction::RefFunc(name) => write!(f, "(ref.func ${})", name),
513            WatInstruction::Return => write!(f, "return"),
514            WatInstruction::ReturnCall(name) => write!(f, "(return_call {name})"),
515            WatInstruction::Block {
516                label,
517                instructions,
518            } => {
519                writeln!(f, "(block {label}")?;
520                for instruction in instructions {
521                    writeln!(f, "  {}", instruction)?;
522                }
523                write!(f, ")")
524            }
525            WatInstruction::Loop {
526                label,
527                instructions,
528            } => {
529                writeln!(f, "(loop {label}")?;
530                for instruction in instructions {
531                    writeln!(f, "  {}", instruction)?;
532                }
533                write!(f, ")")
534            }
535            WatInstruction::If { then, r#else } => {
536                write!(f, "(if (then")?;
537                for instruction in then {
538                    write!(f, " {}", instruction)?;
539                }
540                write!(f, ")")?;
541                if let Some(else_block) = r#else {
542                    write!(f, " (else")?;
543                    for instruction in else_block {
544                        write!(f, " {}", instruction)?;
545                    }
546                    write!(f, ")")?;
547                }
548                write!(f, ")")
549            }
550            WatInstruction::BrIf(label) => write!(f, "(br_if {})", label),
551            WatInstruction::Br(label) => write!(f, "(br {})", label),
552            WatInstruction::Type(name) => write!(f, "{}", name),
553            WatInstruction::Empty => Ok(()),
554            WatInstruction::Log => {
555                writeln!(f, "(call $log)")
556            }
557            WatInstruction::Identifier(s) => write!(f, "{}", s),
558            WatInstruction::Ref(s) => write!(f, "(ref ${})", s),
559            WatInstruction::Drop => writeln!(f, "(drop)"),
560            WatInstruction::LocalTee(name) => write!(f, "(local.tee {})", name),
561
562            WatInstruction::I32Eqz => write!(f, "(i32.eqz)"),
563            WatInstruction::I64Eqz => write!(f, "(i64.eqz)"),
564            WatInstruction::F32Eqz => write!(f, "(f32.eqz)"),
565            WatInstruction::F64Eqz => write!(f, "(f64.eqz)"),
566
567            WatInstruction::RefI31 => write!(f, "(ref.i31)"),
568            WatInstruction::Throw(label) => write!(f, "(throw {label})"),
569            WatInstruction::Try {
570                try_block,
571                catches,
572                catch_all,
573            } => {
574                writeln!(
575                    f,
576                    "\ntry\n{try_block}{}{}\nend",
577                    catches
578                        .iter()
579                        .map(|c| c.to_string())
580                        .collect::<Vec<String>>()
581                        .join(""),
582                    catch_all
583                        .clone()
584                        .map(|c| c.to_string())
585                        .unwrap_or("".to_string())
586                )
587            }
588            WatInstruction::Catch(label, instr) => writeln!(f, "\ncatch {label}\n{instr}"),
589            WatInstruction::CatchAll(instr) => writeln!(f, "\ncatch_all\n{instr}"),
590            WatInstruction::I64ExtendI32S => writeln!(f, "(i64.extend_32_s)"),
591            WatInstruction::I32WrapI64 => writeln!(f, "(i32.wrap_i64)"),
592            WatInstruction::I31GetS => writeln!(f, "(i31.get_s)"),
593            WatInstruction::F64PromoteF32 => writeln!(f, "(f64.promote_f32)"),
594            WatInstruction::F32DemoteF64 => writeln!(f, "(f32.demote_f64)"),
595            WatInstruction::I32Eq => writeln!(f, "(i32.eq)"),
596            WatInstruction::I64Eq => writeln!(f, "(i64.eq)"),
597            WatInstruction::F32Eq => writeln!(f, "(f32.eq)"),
598            WatInstruction::F64Eq => writeln!(f, "(f64.eq)"),
599        }
600    }
601}
602
603#[derive(Debug, Clone)]
604pub struct WatFunction {
605    pub name: String,
606    pub params: Vec<(String, WasmType)>,
607    pub return_type: Option<WasmType>,
608    pub locals: HashMap<String, WasmType>,
609    pub locals_counters: HashMap<String, u32>,
610    pub body: Vec<Box<WatInstruction>>,
611}
612
613impl WatFunction {
614    pub fn new(name: impl Into<String>) -> Self {
615        WatFunction {
616            name: name.into(),
617            params: Vec::new(),
618            return_type: None,
619            locals: HashMap::new(),
620            locals_counters: HashMap::new(),
621            body: Vec::new(),
622        }
623    }
624
625    pub fn add_param(&mut self, name: impl Into<String>, type_: &WasmType) {
626        self.params.push((name.into(), type_.clone()));
627    }
628
629    pub fn set_return_type(&mut self, type_: WasmType) {
630        self.return_type = Some(type_);
631    }
632
633    pub fn add_local_exact(&mut self, name: impl Into<String>, r#type: WasmType) {
634        self.locals.insert(name.into(), r#type);
635    }
636
637    pub fn add_local(&mut self, name: impl Into<String>, r#type: WasmType) -> String {
638        let name = name.into();
639
640        let counter = self.locals_counters.entry(name.clone()).or_insert(0);
641        *counter += 1;
642        let name = format!("{name}-{counter}");
643        self.locals.insert(name.clone(), r#type);
644
645        name
646    }
647
648    pub fn add_instruction(&mut self, instruction: WatInstruction) {
649        self.body.push(Box::new(instruction));
650    }
651}
652
653impl fmt::Display for WatFunction {
654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655        write!(f, "(func ${}", self.name)?;
656        for (name, type_) in &self.params {
657            write!(f, " (param {} {})", name, type_)?;
658        }
659        if let Some(return_type) = &self.return_type {
660            write!(f, " (result {})", return_type)?;
661        }
662        writeln!(f)?;
663        for (name, type_) in &self.locals {
664            writeln!(f, "  (local {} {})", name, type_)?;
665        }
666        for instruction in &self.body {
667            writeln!(f, "  {}", instruction)?;
668        }
669        writeln!(f, ")")
670    }
671}
672
673#[derive(Debug, Clone, Default)]
674pub struct WatModule {
675    pub tags: IndexMap<String, String>,
676    pub types: IndexMap<String, WasmType>,
677    pub imports: Vec<(String, String, WasmType)>,
678    pub functions: Vec<WatFunction>,
679    // TODO: changet it to a struct
680    pub exports: Vec<(String, String, String)>,
681    pub globals: Vec<(String, WasmType, WatInstruction)>,
682    pub data: Vec<(usize, String)>,
683    pub data_offset: usize,
684    pub data_offsets: HashMap<String, usize>,
685    pub memories: HashMap<String, i32>,
686}
687
688impl WatModule {
689    pub fn new() -> Self {
690        WatModule {
691            tags: IndexMap::new(),
692            types: IndexMap::new(),
693            imports: Vec::new(),
694            functions: Vec::new(),
695            exports: Vec::new(),
696            globals: Vec::new(),
697            data: Vec::new(),
698            data_offset: 100,
699            data_offsets: HashMap::new(),
700            memories: HashMap::new(),
701        }
702    }
703
704    pub fn add_function(&mut self, function: WatFunction) {
705        self.functions.push(function);
706    }
707
708    pub fn get_function_mut(&mut self, name: &str) -> Option<&mut WatFunction> {
709        self.functions.iter_mut().find(|f| f.name == name)
710    }
711
712    pub fn add_type(&mut self, name: impl Into<String>, ty: WasmType) {
713        self.types.insert(name.into(), ty);
714    }
715
716    pub fn add_data(&mut self, content: String) -> (usize, usize) {
717        let len = content.len();
718        let offset = self.data_offset;
719        if let Some(offset) = self.data_offsets.get(&content) {
720            (*offset, len)
721        } else {
722            self.data.push((offset, content.clone()));
723            self.data_offsets.insert(content, offset);
724            self.data_offset += if len % 4 == 0 {
725                len
726            } else {
727                // some runtimes expect all data aligned to 4 bytes
728                len + (4 - len % 4)
729            };
730
731            (offset, len)
732        }
733    }
734
735    pub fn add_memory(&mut self, label: impl Into<String>, size: i32) {
736        self.memories.insert(label.into(), size);
737    }
738
739    pub fn add_export(
740        &mut self,
741        export_name: impl Into<String>,
742        export_type: impl Into<String>,
743        name: impl Into<String>,
744    ) {
745        self.exports
746            .push((export_name.into(), export_type.into(), name.into()));
747    }
748
749    pub fn add_import(
750        &mut self,
751        namespace: impl Into<String>,
752        name: impl Into<String>,
753        ty: WasmType,
754    ) {
755        self.imports.push((namespace.into(), name.into(), ty));
756    }
757}
758
759impl fmt::Display for WatModule {
760    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761        writeln!(f, "(module")?;
762        // Types
763        for (name, ty) in &self.types {
764            writeln!(f, "  (type {name} {ty})")?;
765        }
766
767        // Tags
768        for (label, typeidx) in &self.tags {
769            writeln!(f, "  (tag {label} (type {typeidx}))")?;
770        }
771
772        // Imports
773        for (module, name, type_) in &self.imports {
774            writeln!(f, "  (import \"{}\" \"{}\" {})", module, name, type_)?;
775        }
776
777        // Memories
778        for (label, size) in &self.memories {
779            writeln!(f, "  (memory {label} {size})")?;
780        }
781
782        // Data
783        for (offset, data) in &self.data {
784            write!(f, "  (data (i32.const {}) \"", offset)?;
785            // TODO: this escaping should be done when inserting the data
786            for &byte in data.as_bytes() {
787                match byte {
788                    b'"' => write!(f, "\\\"")?,
789                    b'\\' => write!(f, "\\\\")?,
790                    b'\n' => write!(f, "\\n")?,
791                    b'\r' => write!(f, "\\r")?,
792                    b'\t' => write!(f, "\\t")?,
793                    _ if byte.is_ascii_graphic() || byte == b' ' => write!(f, "{}", byte as char)?,
794                    _ => write!(f, "\\{:02x}", byte)?,
795                }
796            }
797            writeln!(f, "\")")?;
798        }
799
800        // Globals
801        for (name, type_, init) in &self.globals {
802            writeln!(f, "  (global ${} {} {})", name, type_, init)?;
803        }
804
805        // Function declarations
806        for function in &self.functions {
807            writeln!(f, "(elem declare func ${})", function.name)?;
808        }
809
810        // Functions
811        for function in &self.functions {
812            write!(f, "  {}", function)?;
813        }
814
815        // Exports
816        for (name, export_type, internal_name) in &self.exports {
817            writeln!(
818                f,
819                "  (export \"{}\" ({export_type} {}))",
820                name, internal_name
821            )?;
822        }
823
824        writeln!(f, ")")?;
825
826        Ok(())
827    }
828}