1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
use super::*;
use wast::core::{ArrayFill, ArrayNewFixed, MemArg, StructAccess};

impl WasmInstruction for Operation {
    fn emit<'a, 'i>(&'a self, w: &mut Vec<Instruction<'i>>)
    where
        'a: 'i,
    {
        match self {
            Self::Sequence { code: items } => {
                items.iter().for_each(|i| i.emit(w));
            }
            Self::Repeats { code, repeats } => {
                for _ in 0..*repeats {
                    code.iter().for_each(|i| i.emit(w));
                }
            }
            Self::GetVariable { kind, variable } => match kind {
                VariableKind::Global => w.push(Instruction::GlobalGet(Index::Id(WasmName::new(variable.as_ref())))),
                VariableKind::Local => w.push(Instruction::LocalGet(Index::Id(WasmName::new(variable.as_ref())))),
                VariableKind::Table => {
                    w.push(Instruction::TableGet(TableArg { dst: Index::Id(WasmName::new(variable.as_ref())) }))
                }
            },
            Self::GetField { structure, field } => {
                w.push(Instruction::StructGet(StructAccess { r#struct: structure.as_index(), field: field.as_index() }))
            }
            Self::SetField { structure, field } => {
                w.push(Instruction::StructSet(StructAccess { r#struct: structure.as_index(), field: field.as_index() }))
            }
            Self::SetVariable { kind, variable } => match kind {
                VariableKind::Global => w.push(Instruction::GlobalSet(Index::Id(WasmName::new(variable.as_ref())))),
                VariableKind::Local => w.push(Instruction::LocalSet(Index::Id(WasmName::new(variable.as_ref())))),
                VariableKind::Table => {
                    w.push(Instruction::TableSet(TableArg { dst: Index::Id(WasmName::new(variable.as_ref())) }))
                }
            },
            Self::TeeVariable { variable } => w.push(Instruction::LocalTee(Index::Id(WasmName::new(variable.as_ref())))),
            Self::CallFunction { name, input } => {
                input.iter().for_each(|i| i.emit(w));
                w.push(Instruction::Call(Index::Id(WasmName::new(name.as_ref()))));
            }
            Self::Default { typed } => typed.emit(w),
            Self::Constant { value } => value.emit(w),
            Self::NativeSum { r#type: native, terms } => match terms.as_slice() {
                [] => {}
                [head, rest @ ..] => {
                    head.emit(w);
                    for i in rest {
                        i.emit(w);
                        match native {
                            WasmType::I32 => w.push(Instruction::I32Add),
                            WasmType::I64 => w.push(Instruction::I64Add),
                            WasmType::F32 => w.push(Instruction::F32Add),
                            WasmType::F64 => w.push(Instruction::F64Add),
                            _ => todo!(),
                        }
                    }
                }
            },
            Self::NativeEqual { r#type: native, codes } => match codes.as_slice() {
                [] => {}
                [head, rest @ ..] => {
                    head.emit(w);
                    for i in rest {
                        i.emit(w);
                        match native {
                            WasmType::Bool | WasmType::I32 => w.push(Instruction::I32Eq),
                            WasmType::I64 => w.push(Instruction::I64Eq),
                            WasmType::F32 => w.push(Instruction::F32Eq),
                            WasmType::F64 => w.push(Instruction::F64Eq),
                            WasmType::Any { nullable } => match nullable {
                                true => w.push(Instruction::RefEq),
                                false => w.push(Instruction::RefEq),
                            },
                            _ => todo!(),
                        }
                    }
                }
            },
            Self::NativeEqualZero { .. } => {
                todo!()
            }
            Self::JumpBranch(branch) => branch.emit(w),
            Self::JumpTable(table) => table.emit(w),
            Self::Loop { r#continue, r#break, body } => {
                w.push(Instruction::Loop(Box::new(BlockType {
                    label: WasmName::id(r#continue.as_ref()),
                    label_name: None,
                    ty: TypeUse { index: None, inline: None },
                })));
                w.push(Instruction::Block(Box::new(BlockType {
                    label: WasmName::id(r#break.as_ref()),
                    label_name: None,
                    ty: TypeUse { index: None, inline: None },
                })));
                body.iter().for_each(|i| i.emit(w));
                w.push(Instruction::End(None));
                w.push(Instruction::End(None));
            }
            Self::Goto { label } => w.push(Instruction::Br(Index::Id(WasmName::new(label.as_ref())))),
            Self::Drop => {
                w.push(Instruction::Drop);
            }
            Self::Return => w.push(Instruction::Return),
            Self::Unreachable => w.push(Instruction::Unreachable),
            Self::Convert { from, into, code } => {
                code.iter().for_each(|i| i.emit(w));
                match (from, into) {
                    // u32 -> ?
                    (WasmType::U32, WasmType::U32) => {}
                    (WasmType::U32, WasmType::I32) => w.push(Instruction::I32WrapI64),
                    (WasmType::U32, WasmType::I64) => w.push(Instruction::I64ExtendI32U),
                    (WasmType::U32, WasmType::F32) => w.push(Instruction::F32ConvertI32U),
                    (WasmType::U32, WasmType::F64) => w.push(Instruction::F64ConvertI32U),
                    // i32 -> ?
                    (WasmType::I32, WasmType::I32) => {}
                    (WasmType::I32, WasmType::I64) => w.push(Instruction::I64ExtendI32S),
                    (WasmType::I32, WasmType::F32) => w.push(Instruction::F32ConvertI32S),
                    (WasmType::I64, WasmType::F32) => w.push(Instruction::F32ConvertI64S),
                    // f32 -> ?
                    (WasmType::F32, WasmType::I32) => w.push(Instruction::I32TruncF32S),
                    (WasmType::F32, WasmType::I64) => w.push(Instruction::I64TruncF32S),
                    (WasmType::F32, WasmType::F32) => {}
                    (WasmType::F32, WasmType::F64) => w.push(Instruction::F64PromoteF32),
                    // f64 -> ?
                    (WasmType::F64, WasmType::I32) => w.push(Instruction::I32TruncF64S),
                    (WasmType::F64, WasmType::I64) => w.push(Instruction::I64TruncF64S),
                    (WasmType::F64, WasmType::F32) => w.push(Instruction::F32DemoteF64),
                    (WasmType::F64, WasmType::F64) => {}
                    _ => {
                        unimplemented!()
                    }
                }
            }
            Self::Transmute { from, into, code } => {
                code.iter().for_each(|i| i.emit(w));
                match (from, into) {
                    (WasmType::I32, WasmType::F32) => w.push(Instruction::F32ReinterpretI32),
                    (WasmType::I64, WasmType::F64) => w.push(Instruction::F64ReinterpretI64),
                    (WasmType::F32, WasmType::I32) => w.push(Instruction::I32ReinterpretF32),
                    (WasmType::F64, WasmType::I64) => w.push(Instruction::I64ReinterpretF64),

                    _ => {
                        unimplemented!()
                    }
                }
            }
            Self::JumpEnumeration(_) => {}
            Self::StoreVariable { r#type, offset } => {
                let memory = Index::Id(WasmName::new("memory"));
                match r#type {
                    WasmType::Bool => {}
                    WasmType::U8 => {}
                    WasmType::U16 => {}
                    WasmType::U32 => {}
                    WasmType::U64 => {}
                    WasmType::I8 => {}
                    WasmType::I16 => w.push(Instruction::I64Store16(MemArg { align: 2, offset: *offset, memory })),
                    WasmType::I32 => w.push(Instruction::I32Store(MemArg { align: 4, offset: *offset, memory })),
                    WasmType::I64 => w.push(Instruction::I64Store(MemArg { align: 8, offset: *offset, memory })),
                    WasmType::F32 => w.push(Instruction::F32Store(MemArg { align: 4, offset: *offset, memory })),
                    WasmType::F64 => w.push(Instruction::F64Store(MemArg { align: 8, offset: *offset, memory })),
                    _ => unimplemented!(),
                }
            }
            Self::Construct { structure } => w.push(Instruction::StructNew(structure.as_index())),
            Self::GetIndex { r#type, index, object } => {
                object.iter().for_each(|i| i.emit(w));
                w.push(Instruction::I32Const(*index));
                w.push(Instruction::ArrayGet(r#type.symbol.as_index()))
            }
            Self::ArrayCreate { r#type, element } => {
                element.iter().for_each(|i| i.emit(w));
                w.push(Instruction::ArrayNewFixed(ArrayNewFixed {
                    array: r#type.symbol.as_index(),
                    length: element.len() as u32,
                }))
            }
            Self::ArrayFill { array, r#type, element, start, length } => {
                array.iter().for_each(|i| i.emit(w));
                w.push(Instruction::I32Const(*start as i32));
                element.iter().for_each(|i| i.emit(w));
                w.push(Instruction::I32Const(*length as i32));
                w.push(Instruction::ArrayFill(ArrayFill { array: r#type.symbol.as_index() }));
            }
            Self::ArrayLength { object } => {
                object.iter().for_each(|i| i.emit(w));
                w.push(Instruction::ArrayLen)
            }
            Self::ArrayGrow { .. } => {
                todo!()
            }
        }
    }
}

fn block_return(returns: &[WasmType]) -> Option<wast::core::FunctionType> {
    if returns.is_empty() {
        None
    }
    else {
        let result: Vec<_> = returns.iter().map(|i| i.as_wast()).collect();
        Some(wast::core::FunctionType { params: Box::default(), results: Box::from(result) })
    }
}

impl WasmInstruction for JumpBranch {
    /// `if { then } else { else } end`
    /// `{ then } { else } condition select`
    fn emit<'a, 'i>(&'a self, w: &mut Vec<Instruction<'i>>)
    where
        'a: 'i,
    {
        let inline = block_return(&self.r#return);
        self.main.condition.iter().for_each(|i| i.emit(w));
        w.push(Instruction::If(Box::new(BlockType { label: None, label_name: None, ty: TypeUse { index: None, inline } })));
        self.main.action.iter().for_each(|i| i.emit(w));
        w.push(Instruction::Else(None));
        self.default.iter().for_each(|i| i.emit(w));
        w.push(Instruction::End(None))
    }
}

impl WasmInstruction for JumpTable {
    /// ```v
    /// if a { a_body }
    /// else if b { b_body }
    /// else { c_body }
    /// ```
    ///
    /// ```v
    /// a
    /// if
    ///   a_body
    /// else  
    ///   b
    ///   if
    ///     b_body
    ///   else
    ///     c_body
    ///   end
    /// end
    /// ```
    fn emit<'a, 'i>(&'a self, w: &mut Vec<Instruction<'i>>)
    where
        'a: 'i,
    {
        let inline = block_return(&self.r#return);
        for branch in &self.branches {
            branch.condition.iter().for_each(|i| i.emit(w));
            w.push(Instruction::If(Box::new(BlockType {
                label: None,
                label_name: None,
                ty: TypeUse { index: None, inline: inline.clone() },
            })));
            branch.action.iter().for_each(|i| i.emit(w));
            w.push(Instruction::Else(None));
        }
        self.default.iter().for_each(|i| i.emit(w));
        for _ in &self.branches {
            w.push(Instruction::End(None));
        }
    }
}
impl WasmInstruction for WasmType {
    fn emit<'a, 'i>(&'a self, w: &mut Vec<Instruction<'i>>)
    where
        'a: 'i,
    {
        match self {
            // false
            Self::Bool => w.push(Instruction::I32Const(0)),
            Self::U8 => w.push(Instruction::I32Const(0)),
            Self::U16 => w.push(Instruction::I32Const(0)),
            Self::U32 => w.push(Instruction::I32Const(0)),
            Self::U64 => w.push(Instruction::I64Const(0)),
            Self::I8 => w.push(Instruction::I32Const(0)),
            Self::I16 => w.push(Instruction::I32Const(0)),
            Self::I32 => w.push(Instruction::I32Const(0)),
            Self::I64 => w.push(Instruction::I64Const(0)),
            Self::F32 => w.push(Instruction::F32Const(Float32 { bits: 0 })),
            Self::F64 => w.push(Instruction::F64Const(Float64 { bits: 0 })),
            Self::Any { .. } => {
                todo!()
            }
            Self::Structure(s) => w.push(Instruction::StructNewDefault(s.symbol.as_index())),
            Self::Array(t) => {
                w.push(Instruction::I32Const(0));
                w.push(Instruction::ArrayNewDefault(t.symbol.as_index()))
            }
            Self::Unicode => {
                todo!()
            }
            Self::UTF8Text => {
                todo!()
            }
            Self::Enumerate(_) => todo!(),
            Self::Flag(_) => todo!(),
            Self::Variant(_) => todo!(),
        }
    }
}

impl WasmInstruction for WasmValue {
    fn emit<'a, 'i>(&'a self, w: &mut Vec<Instruction<'i>>)
    where
        'a: 'i,
    {
        match self {
            Self::Bool(v) => match v {
                true => w.push(Instruction::I32Const(1)),
                false => w.push(Instruction::I32Const(0)),
            },
            Self::U32(v) => w.push(Instruction::I32Const(*v as i32)),
            Self::I32(v) => w.push(Instruction::I32Const(*v)),
            Self::I64(v) => w.push(Instruction::I64Const(*v)),
            Self::F32(v) => w.push(Instruction::F32Const(Float32 { bits: u32::from_le_bytes(v.to_le_bytes()) })),
            Self::F64(v) => w.push(Instruction::F64Const(Float64 { bits: u64::from_le_bytes(v.to_le_bytes()) })),
            Self::Function(_) => {
                todo!()
            }
            Self::Structure(v) => {
                for x in v.fields.values() {
                    x.default.emit(w)
                }
                w.push(Instruction::StructNew(v.symbol.as_index()))
            }
            Self::Array(v) => w.push(Instruction::ArrayNew(v.symbol.as_index())),
            Self::Any => {
                todo!()
            }
        }
    }
}