1
#![allow(missing_docs, clippy::missing_panics_doc)]
2
//! Bud Assembly Language
3
//!
4
//! ```budasm
5
//! fn name @a @b @c
6
//! // (a * b) * c
7
//! mul @a @b $tmp
8
//! mul $tmp @c $
9
//!
10
//! fn __init
11
//! // init function called when module is loaded
12
//! // name(1, 2, 3) -> 6
13
//! push 1
14
//! push 2
15
//! push 3
16
//! call name 3
17
//! ```
18
//!
19
// We use the Range type which doesn't allow this, and it's not worth making a
20
// helper function to appease clippy.
21
#![allow(clippy::range_plus_one)]
22

            
23
use std::{
24
    error::Error,
25
    fmt::Display,
26
    iter::Peekable,
27
    ops::Range,
28
    str::{CharIndices, FromStr},
29
};
30

            
31
use crate::{
32
    ir::{
33
        CodeBlockBuilder, CompareAction, Destination, Function, Instruction, Label, Literal,
34
        LiteralOrSource, Module,
35
    },
36
    lexer_util::{
37
        decode_numeric_literal, decode_string_literal_contents, DecodeNumericError,
38
        DecodeStringError, DoublePeekable, Numeric,
39
    },
40
    Comparison, Symbol, ValueKind,
41
};
42

            
43
#[derive(PartialEq, Debug)]
44
pub struct Token {
45
    pub kind: TokenKind,
46
    pub range: Range<usize>,
47
}
48

            
49
impl Token {
50
327
    fn at_offset(kind: TokenKind, offset: usize) -> Self {
51
327
        Self::new(kind, offset..offset + 1)
52
327
    }
53

            
54
553
    fn new(kind: TokenKind, range: Range<usize>) -> Self {
55
553
        Self { kind, range }
56
553
    }
57
}
58

            
59
16
#[derive(Debug, PartialEq)]
60
pub enum TokenKind {
61
    Identifier(Symbol),
62
    EndOfLine,
63
    Comment,
64
    Integer(i64),
65
    Real(f64),
66
    String(String),
67
}
68

            
69
pub struct Lexer<'a> {
70
    chars: DoublePeekable<CharIndices<'a>>,
71
    source: &'a str,
72
}
73

            
74
impl<'a> Lexer<'a> {
75
    #[must_use]
76
14
    pub fn new(source: &'a str) -> Self {
77
14
        Self {
78
14
            chars: DoublePeekable::new(source.char_indices()),
79
14
            source,
80
14
        }
81
14
    }
82
}
83

            
84
impl<'a> Iterator for Lexer<'a> {
85
    type Item = Result<Token, AsmError>;
86

            
87
1017
    fn next(&mut self) -> Option<Self::Item> {
88
        loop {
89
4554
            match self.chars.next()? {
90
4540
                (offset, ch) if ch == '$' || ch == '@' || ch == '#' || ch.is_alphabetic() => {
91
450
                    let mut end = offset;
92
2620
                    while self.chars.peek().map_or(false, |(_, ch)| {
93
2619
                        ch.is_alphanumeric() || *ch == '_' || *ch == '$'
94
2620
                    }) {
95
2170
                        end = self.chars.next().expect("just peeked").0;
96
2170
                    }
97
450
                    return Some(Ok(Token {
98
450
                        kind: TokenKind::Identifier(Symbol::from(&self.source[offset..=end])),
99
450
                        range: offset..end + 1,
100
450
                    }));
101
                }
102
4090
                (offset, char) if char == '\r' || char == '\n' => {
103
327
                    if char == '\r' && matches!(self.chars.peek().map(|(_, ch)| *ch), Some('\n')) {
104
                        return Some(Ok(Token::new(TokenKind::EndOfLine, offset..offset + 2)));
105
327
                    }
106
327

            
107
327
                    return Some(Ok(Token::at_offset(TokenKind::EndOfLine, offset)));
108
                }
109
3763
                (offset, char) if char == '"' => {
110
1
                    let literal = match decode_string_literal_contents(&mut self.chars, offset) {
111
1
                        Ok(literal) => literal,
112
                        Err(err) => return Some(Err(AsmError::from(err))),
113
                    };
114

            
115
1
                    return Some(Ok(Token::new(
116
1
                        TokenKind::String(literal.contents),
117
1
                        offset..literal.end_quote_offset + 1,
118
1
                    )));
119
                }
120
104
                (offset, char)
121
3762
                    if char.is_numeric()
122
3658
                        || (char == '-'
123
                            && self.chars.peek().map_or(false, |(_, ch)| ch.is_numeric())) =>
124
                {
125
104
                    let literal = match decode_numeric_literal(&mut self.chars, self.source, offset)
126
                    {
127
104
                        Ok(literal) => literal,
128
                        Err(err) => return Some(Err(AsmError::from(err))),
129
                    };
130

            
131
                    return Some(Ok(Token::new(
132
104
                        match literal.contents {
133
103
                            Numeric::Integer(integer) => TokenKind::Integer(integer),
134
1
                            Numeric::Real(real) => TokenKind::Real(real),
135
                        },
136
104
                        offset..literal.last_offset + 1,
137
                    )));
138
                }
139
121
                (offset, char)
140
3658
                    if char == '/' && matches!(self.chars.peek().map(|(_, ch)| *ch), Some('/')) =>
141
                {
142
                    // Comment
143
121
                    let (mut end, _) = self.chars.next().expect("just peeked");
144

            
145
                    // Read until end of line
146
5288
                    while self
147
5288
                        .chars
148
5288
                        .peek()
149
5288
                        .map_or(false, |(_, char)| *char != '\n' && *char != '\r')
150
5167
                    {
151
5167
                        end = self.chars.next().expect("just peeked").0;
152
5167
                    }
153

            
154
121
                    return Some(Ok(Token::new(TokenKind::Comment, offset..end + 1)));
155
                }
156
3537
                (_, ch) if ch.is_ascii_whitespace() => {}
157
                (offset, character) => {
158
                    return Some(Err(AsmError::UnexpectedChar { character, offset }))
159
                }
160
            }
161
        }
162
1017
    }
163
}
164

            
165
#[derive(PartialEq, Debug)]
166
pub enum AsmError {
167
    String(DecodeStringError),
168
    Numeric(DecodeNumericError),
169
    UnexpectedChar { character: char, offset: usize },
170
    Unexpected(Token),
171
    UnexpectedEof(String),
172
    LabelAlreadyUsed { label: Symbol, range: Range<usize> },
173
    UnknownInstruction(Token),
174
    UnknownArgument(Token),
175
    InvalidArgumentCount(Token),
176
    UnknownIntrinsic(Token),
177
}
178

            
179
impl From<DecodeStringError> for AsmError {
180
    fn from(err: DecodeStringError) -> Self {
181
        Self::String(err)
182
    }
183
}
184

            
185
impl From<DecodeNumericError> for AsmError {
186
    fn from(err: DecodeNumericError) -> Self {
187
        Self::Numeric(err)
188
    }
189
}
190

            
191
impl Display for AsmError {
192
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193
        match self {
194
            AsmError::String(error) => write!(f, "{error}"),
195
            AsmError::Numeric(error) => write!(f, "{error}"),
196
            AsmError::UnexpectedChar { character, offset } => {
197
                write!(f, "unexpected char `{character}` at {offset}")
198
            }
199
            AsmError::Unexpected(token) => write!(f, "unexpected token `{token:?}`"),
200
            AsmError::UnexpectedEof(expected) => write!(f, "unexpected eof, expected `{expected}`"),
201
            AsmError::LabelAlreadyUsed { label, range } => {
202
                write!(f, "label already used `{label}` at {range:?}")
203
            }
204
            AsmError::UnknownInstruction(token) => write!(f, "unknown instruction `{token:?}`"),
205
            AsmError::UnknownArgument(token) => write!(f, "unknown argument `{token:?}`"),
206
            AsmError::InvalidArgumentCount(token) => {
207
                write!(f, "invalid argument count `{token:?}`")
208
            }
209
            AsmError::UnknownIntrinsic(token) => write!(f, "unknown intrinsic `{token:?}`"),
210
        }
211
    }
212
}
213

            
214
impl Error for AsmError {
215
    fn source(&self) -> Option<&(dyn Error + 'static)> {
216
        match self {
217
            AsmError::String(error) => Some(error),
218
            AsmError::Numeric(error) => Some(error),
219
            _ => None,
220
        }
221
    }
222
}
223

            
224
pub(crate) struct Parser<'a, Intrinsic> {
225
    tokens: Peekable<Lexer<'a>>,
226
    current_function_name: Option<Symbol>,
227
    current_function: CodeBlockBuilder<Intrinsic>,
228
    module: Module<Intrinsic>,
229
}
230

            
231
impl<'a, Intrinsic> Parser<'a, Intrinsic>
232
where
233
    Intrinsic: FromStr + Display,
234
{
235
3
    pub fn parse(asm: &'a str) -> Result<Module<Intrinsic>, AsmError> {
236
3
        Self {
237
3
            tokens: Lexer::new(asm).peekable(),
238
3
            current_function_name: None,
239
3
            current_function: CodeBlockBuilder::default(),
240
3
            module: Module::default(),
241
3
        }
242
3
        .parse_asm()
243
3
    }
244

            
245
3
    fn parse_asm(mut self) -> Result<Module<Intrinsic>, AsmError> {
246
118
        while let Some(token) = self.tokens.next().transpose()? {
247
115
            match &token.kind {
248
49
                TokenKind::Identifier(symbol) => {
249
49
                    if let Some(label) = symbol.strip_prefix('#') {
250
2
                        let label = self.current_function.named_label(label);
251
2
                        self.current_function.label(label);
252
2
                        self.expect_end_of_line()?;
253
                    } else {
254
47
                        match &**symbol {
255
47
                            "function" => self.parse_function()?,
256
45
                            "add" | "sub" | "mul" | "div" | "and" | "or" | "xor" | "bitor"
257
34
                            | "bitand" | "bitxor" | "shl" | "shr" => self.parse_binop(symbol)?,
258
30
                            "eq" | "neq" | "lt" | "lte" | "gt" | "gte" => {
259
7
                                self.parse_comparison(symbol)?;
260
                            }
261
23
                            "not" => self.parse_not()?,
262
22
                            "bitnot" => self.parse_bitnot()?,
263
21
                            "convert" => self.parse_convert()?,
264
17
                            "ifnot" => self.parse_ifnot()?,
265
16
                            "jump" => self.parse_jump()?,
266
15
                            "push" => self.parse_push()?,
267
13
                            "load" => self.parse_load()?,
268
12
                            "call" => self.parse_call()?,
269
10
                            "recurse" => self.parse_call_with_name(None)?,
270
7
                            "intrinsic" => self.parse_intrinsic()?,
271
6
                            "invoke" => self.parse_invoke()?,
272
4
                            "return" => {
273
4
                                let value = if self.next_is_end_of_line() {
274
1
                                    None
275
                                } else {
276
3
                                    Some(self.expect_literal_or_source()?)
277
                                };
278
4
                                self.expect_end_of_line()?;
279
4
                                self.current_function.push(Instruction::Return(value));
280
                            }
281
                            _ => return Err(AsmError::UnknownInstruction(token)),
282
                        }
283
                    }
284
                }
285
66
                TokenKind::EndOfLine | TokenKind::Comment => {}
286
                _ => return Err(AsmError::Unexpected(token)),
287
            }
288
        }
289

            
290
3
        self.begin_new_function(None);
291
3

            
292
3
        Ok(self.module)
293
3
    }
294

            
295
15
    fn parse_binop(&mut self, kind: &Symbol) -> Result<(), AsmError> {
296
15
        let left = self.expect_literal_or_source()?;
297
15
        let right = self.expect_literal_or_source()?;
298
15
        let destination = self.expect_destination()?;
299

            
300
15
        let instruction = match &**kind {
301
15
            "add" => Instruction::Add {
302
2
                left,
303
2
                right,
304
2
                destination,
305
2
            },
306
13
            "sub" => Instruction::Sub {
307
3
                left,
308
3
                right,
309
3
                destination,
310
3
            },
311
10
            "mul" => Instruction::Multiply {
312
1
                left,
313
1
                right,
314
1
                destination,
315
1
            },
316
9
            "div" => Instruction::Divide {
317
1
                left,
318
1
                right,
319
1
                destination,
320
1
            },
321
8
            "and" => Instruction::LogicalAnd {
322
1
                left,
323
1
                right,
324
1
                destination,
325
1
            },
326
7
            "or" => Instruction::LogicalOr {
327
1
                left,
328
1
                right,
329
1
                destination,
330
1
            },
331
6
            "xor" => Instruction::LogicalXor {
332
1
                left,
333
1
                right,
334
1
                destination,
335
1
            },
336
5
            "bitand" => Instruction::BitwiseAnd {
337
1
                left,
338
1
                right,
339
1
                destination,
340
1
            },
341
4
            "bitor" => Instruction::BitwiseOr {
342
1
                left,
343
1
                right,
344
1
                destination,
345
1
            },
346
3
            "bitxor" => Instruction::BitwiseXor {
347
1
                left,
348
1
                right,
349
1
                destination,
350
1
            },
351
2
            "shl" => Instruction::ShiftLeft {
352
1
                left,
353
1
                right,
354
1
                destination,
355
1
            },
356
1
            "shr" => Instruction::ShiftRight {
357
1
                left,
358
1
                right,
359
1
                destination,
360
1
            },
361
            _ => unreachable!("this list should match the one found in parse_asm"),
362
        };
363
15
        self.current_function.push(instruction);
364
15
        Ok(())
365
15
    }
366

            
367
5
    fn begin_new_function(&mut self, new_function_name: Option<Symbol>) {
368
5
        let block = std::mem::take(&mut self.current_function).finish();
369
5
        match std::mem::replace(&mut self.current_function_name, new_function_name) {
370
2
            Some(name) => self.module.vtable.push(Function::new(name, block)),
371
3
            None => {
372
3
                self.module.init = Some(Function::new(Symbol::from("__init"), block));
373
3
            }
374
        }
375
5
    }
376

            
377
4
    fn next_is_end_of_line(&mut self) -> bool {
378
4
        self.peek_token_kind().map_or(true, |kind| {
379
4
            matches!(kind, TokenKind::EndOfLine | TokenKind::Comment)
380
4
        })
381
4
    }
382

            
383
    fn expect_end_of_line(&mut self) -> Result<(), AsmError> {
384
6
        match self.tokens.next().transpose()? {
385
6
            Some(token) if token.kind == TokenKind::Comment => {
386
                // Skip comments
387
                self.expect_end_of_line()
388
            }
389
6
            Some(token) if token.kind == TokenKind::EndOfLine => Ok(()),
390
            None => Ok(()),
391
            Some(token) => Err(AsmError::Unexpected(token)),
392
        }
393
6
    }
394

            
395
    fn expect_next(&mut self, expected: &str) -> Result<Token, AsmError> {
396
117
        self.tokens
397
117
            .next()
398
117
            .ok_or_else(|| AsmError::UnexpectedEof(expected.to_string()))?
399
117
    }
400

            
401
12
    fn expect_identifier(&mut self, expected: &str) -> Result<(Symbol, Range<usize>), AsmError> {
402
12
        let token = self.expect_next(expected)?;
403
12
        match token.kind {
404
12
            TokenKind::Identifier(symbol) => Ok((symbol, token.range)),
405
            kind => Err(AsmError::Unexpected(Token {
406
                kind,
407
                range: token.range,
408
            })),
409
        }
410
12
    }
411

            
412
2
    fn expect_label(&mut self) -> Result<Label, AsmError> {
413
2
        let (label, range) = self.expect_identifier("label")?;
414
2
        if let Some(label) = label.strip_prefix('#') {
415
2
            Ok(self.current_function.named_label(label))
416
        } else {
417
            Err(AsmError::Unexpected(Token {
418
                kind: TokenKind::Identifier(label),
419
                range,
420
            }))
421
        }
422
2
    }
423

            
424
8
    fn expect_integer(&mut self, expecting: &str) -> Result<(i64, Range<usize>), AsmError> {
425
8
        let token = self.expect_next(expecting)?;
426
8
        match token.kind {
427
8
            TokenKind::Integer(value) => Ok((value, token.range)),
428
            kind => Err(AsmError::Unexpected(Token {
429
                kind,
430
                range: token.range,
431
            })),
432
        }
433
8
    }
434

            
435
8
    fn expect_arg_count(&mut self) -> Result<usize, AsmError> {
436
8
        let (value, range) = self.expect_integer("arg count")?;
437
8
        match usize::try_from(value) {
438
8
            Ok(value) => Ok(value),
439
            Err(_) => Err(AsmError::InvalidArgumentCount(Token {
440
                kind: TokenKind::Integer(value),
441
                range,
442
            })),
443
        }
444
8
    }
445

            
446
11
    fn peek_token_kind(&mut self) -> Option<&TokenKind> {
447
11
        self.tokens.peek().and_then(|token| {
448
11
            if let Ok(token) = token {
449
11
                Some(&token.kind)
450
            } else {
451
                None
452
            }
453
11
        })
454
11
    }
455

            
456
59
    fn expect_literal_or_source(&mut self) -> Result<LiteralOrSource, AsmError> {
457
59
        let token = self.expect_next("literal, argument, or variable")?;
458
59
        match token.kind {
459
50
            TokenKind::Identifier(symbol) => match &*symbol {
460
50
                "void" => Ok(LiteralOrSource::Literal(Literal::Void)),
461
12
                "true" => Ok(LiteralOrSource::Literal(Literal::Boolean(true))),
462
11
                "false" => Ok(LiteralOrSource::Literal(Literal::Boolean(false))),
463
                _ => {
464
10
                    let (first_char, remaining) = symbol.split_at(1);
465
6
                    match first_char {
466
10
                        "$" if remaining.is_empty() => Ok(LiteralOrSource::Stack),
467
                        "$" => {
468
4
                            let variable = self
469
4
                                .current_function
470
4
                                .variable_index_from_name(&Symbol::from(remaining));
471
4
                            Ok(LiteralOrSource::Variable(variable))
472
                        }
473
4
                        "@" => {
474
4
                            if let Some(arg) =
475
4
                                self.current_function.argument(&Symbol::from(remaining))
476
                            {
477
4
                                Ok(LiteralOrSource::Argument(arg))
478
                            } else {
479
                                Err(AsmError::UnknownArgument(Token {
480
                                    kind: TokenKind::Identifier(symbol),
481
                                    range: token.range,
482
                                }))
483
                            }
484
                        }
485
                        _ => Err(AsmError::Unexpected(Token {
486
                            kind: TokenKind::Identifier(symbol),
487
                            range: token.range,
488
                        })),
489
                    }
490
                }
491
            },
492
7
            TokenKind::Integer(value) => Ok(LiteralOrSource::Literal(Literal::Integer(value))),
493
1
            TokenKind::Real(value) => Ok(LiteralOrSource::Literal(Literal::Real(value))),
494
1
            TokenKind::String(value) => Ok(LiteralOrSource::Literal(Literal::String(value))),
495
            kind => Err(AsmError::Unexpected(Token {
496
                kind,
497
                range: token.range,
498
            })),
499
        }
500
59
    }
501

            
502
34
    fn expect_destination(&mut self) -> Result<Destination, AsmError> {
503
34
        let token = self.expect_next("variable, stack ($) or return ($$)")?;
504
34
        if let TokenKind::Identifier(symbol) = &token.kind {
505
34
            if let Some(remaining) = symbol.strip_prefix('$') {
506
34
                return match remaining {
507
34
                    "" => Ok(Destination::Stack),
508
5
                    "$" => Ok(Destination::Return),
509
                    _ => {
510
3
                        let variable = self
511
3
                            .current_function
512
3
                            .variable_index_from_name(&Symbol::from(remaining));
513
3
                        Ok(Destination::Variable(variable))
514
                    }
515
                };
516
            }
517
        }
518
        Err(AsmError::Unexpected(token))
519
34
    }
520

            
521
2
    fn parse_function(&mut self) -> Result<(), AsmError> {
522
2
        let name = self.expect_next("function name")?;
523
2
        if let TokenKind::Identifier(name) = &name.kind {
524
2
            self.begin_new_function(Some(name.clone()));
525
2
        } else {
526
            return Err(AsmError::Unexpected(name));
527
        }
528

            
529
4
        while let Some(token) = match self.tokens.next().transpose()? {
530
4
            Some(token) if token.kind == TokenKind::EndOfLine => None,
531
2
            Some(token) => Some(token),
532
            None => None,
533
        } {
534
2
            if let TokenKind::Identifier(arg_name) = &token.kind {
535
2
                if let Some(arg_name) = arg_name.strip_prefix('@') {
536
2
                    self.current_function.new_argument(arg_name);
537
2
                } else {
538
                    //
539
                    return Err(AsmError::Unexpected(token));
540
                }
541
            } else {
542
                return Err(AsmError::Unexpected(token));
543
            }
544
        }
545

            
546
2
        Ok(())
547
2
    }
548

            
549
7
    fn parse_comparison(&mut self, kind: &Symbol) -> Result<(), AsmError> {
550
7
        let comparison = match &**kind {
551
7
            "eq" => Comparison::Equal,
552
6
            "neq" => Comparison::NotEqual,
553
5
            "lt" => Comparison::LessThan,
554
4
            "lte" => Comparison::LessThanOrEqual,
555
2
            "gt" => Comparison::GreaterThan,
556
1
            "gte" => Comparison::GreaterThanOrEqual,
557
            _ => unreachable!("list should match parse_asm"),
558
        };
559
7
        let left = self.expect_literal_or_source()?;
560
7
        let right = self.expect_literal_or_source()?;
561
7
        match self.peek_token_kind() {
562
7
            Some(TokenKind::Identifier(action)) => {
563
7
                let action = if action == "jump" {
564
2
                    self.tokens.next();
565

            
566
2
                    let label = self.expect_next("label")?;
567
2
                    if let TokenKind::Identifier(label_name) = &label.kind {
568
2
                        if let Some(label) = label_name.strip_prefix('#') {
569
2
                            let label = self.current_function.named_label(label);
570
2
                            CompareAction::JumpIfFalse(label)
571
                        } else {
572
                            return Err(AsmError::Unexpected(label));
573
                        }
574
                    } else {
575
                        return Err(AsmError::Unexpected(label));
576
                    }
577
                } else {
578
5
                    let destination = self.expect_destination()?;
579
5
                    CompareAction::Store(destination)
580
                };
581

            
582
7
                self.current_function.push(Instruction::Compare {
583
7
                    comparison,
584
7
                    left,
585
7
                    right,
586
7
                    action,
587
7
                });
588
7

            
589
7
                Ok(())
590
            }
591
            Some(_) => Err(AsmError::Unexpected(
592
                self.tokens
593
                    .next()
594
                    .expect("just peeked")
595
                    .expect("just peeked"),
596
            )),
597
            // There could be a parse error, so we use expect_next to raise the
598
            // appropriate error.
599
            None => self.expect_next("compare action").map(|_| ()),
600
        }
601
7
    }
602

            
603
1
    fn parse_not(&mut self) -> Result<(), AsmError> {
604
1
        let value = self.expect_literal_or_source()?;
605
1
        let destination = self.expect_destination()?;
606
1
        self.current_function
607
1
            .push(Instruction::LogicalNot { value, destination });
608
1
        Ok(())
609
1
    }
610

            
611
1
    fn parse_bitnot(&mut self) -> Result<(), AsmError> {
612
1
        let value = self.expect_literal_or_source()?;
613
1
        let destination = self.expect_destination()?;
614
1
        self.current_function
615
1
            .push(Instruction::BitwiseNot { value, destination });
616
1
        Ok(())
617
1
    }
618

            
619
4
    fn parse_convert(&mut self) -> Result<(), AsmError> {
620
4
        let value = self.expect_literal_or_source()?;
621
4
        let (kind, _) = self.expect_identifier("value kind")?;
622
4
        let kind = ValueKind::from(kind);
623
4
        let destination = self.expect_destination()?;
624
4
        self.current_function.push(Instruction::Convert {
625
4
            value,
626
4
            kind,
627
4
            destination,
628
4
        });
629
4
        Ok(())
630
4
    }
631

            
632
1
    fn parse_ifnot(&mut self) -> Result<(), AsmError> {
633
1
        let condition = self.expect_literal_or_source()?;
634
1
        let false_jump_to = self.expect_label()?;
635
1
        self.current_function.push(Instruction::If {
636
1
            condition,
637
1
            false_jump_to,
638
1
        });
639
1
        Ok(())
640
1
    }
641

            
642
1
    fn parse_jump(&mut self) -> Result<(), AsmError> {
643
1
        let label = self.expect_label()?;
644
1
        self.current_function.push(Instruction::JumpTo(label));
645
1
        Ok(())
646
1
    }
647

            
648
2
    fn parse_push(&mut self) -> Result<(), AsmError> {
649
2
        let value = self.expect_literal_or_source()?;
650
2
        self.current_function.push(Instruction::Push(value));
651
2
        Ok(())
652
2
    }
653
1
    fn parse_load(&mut self) -> Result<(), AsmError> {
654
1
        let value = self.expect_literal_or_source()?;
655
1
        let (variable, variable_range) = self.expect_identifier("variable")?;
656
1
        if let Some(variable) = variable.strip_prefix('$') {
657
1
            let variable = self
658
1
                .current_function
659
1
                .variable_index_from_name(&Symbol::from(variable));
660
1
            self.current_function
661
1
                .push(Instruction::Load { value, variable });
662
1
            Ok(())
663
        } else {
664
            Err(AsmError::Unexpected(Token {
665
                kind: TokenKind::Identifier(variable),
666
                range: variable_range,
667
            }))
668
        }
669
1
    }
670

            
671
2
    fn parse_call(&mut self) -> Result<(), AsmError> {
672
2
        let (function, _) = self.expect_identifier("function name")?;
673
2
        self.parse_call_with_name(Some(function))
674
2
    }
675

            
676
5
    fn parse_call_with_name(&mut self, function: Option<Symbol>) -> Result<(), AsmError> {
677
5
        let arg_count = self.expect_arg_count()?;
678
5
        let destination = self.expect_destination()?;
679

            
680
5
        self.current_function.push(Instruction::Call {
681
5
            function,
682
5
            arg_count,
683
5
            destination,
684
5
        });
685
5

            
686
5
        Ok(())
687
5
    }
688

            
689
1
    fn parse_intrinsic(&mut self) -> Result<(), AsmError> {
690
1
        let (name, range) = self.expect_identifier("intrinsic name")?;
691
1
        let intrinsic = <Intrinsic as FromStr>::from_str(&name).map_err(|_| {
692
            AsmError::UnknownIntrinsic(Token {
693
                kind: TokenKind::Identifier(name),
694
                range,
695
            })
696
1
        })?;
697

            
698
1
        let arg_count = self.expect_arg_count()?;
699
1
        let destination = self.expect_destination()?;
700
1
        self.current_function.push(Instruction::CallIntrinsic {
701
1
            intrinsic,
702
1
            arg_count,
703
1
            destination,
704
1
        });
705
1

            
706
1
        Ok(())
707
1
    }
708

            
709
2
    fn parse_invoke(&mut self) -> Result<(), AsmError> {
710
2
        let target = self.expect_literal_or_source()?;
711

            
712
2
        let (name, _) = self.expect_identifier("function name")?;
713
2
        let arg_count = self.expect_arg_count()?;
714
2
        let destination = self.expect_destination()?;
715

            
716
2
        self.current_function.push(Instruction::CallInstance {
717
2
            target,
718
2
            name,
719
2
            arg_count,
720
2
            destination,
721
2
        });
722
2

            
723
2
        Ok(())
724
2
    }
725
}
726

            
727
1
#[test]
728
1
fn basic() {
729
1
    let block: Module<crate::Noop> = Parser::parse(
730
1
        r#"
731
1
            // This is a comment.
732
1
            return 42
733
1
        "#,
734
1
    )
735
1
    .unwrap();
736
1

            
737
1
    assert_eq!(
738
1
        block.init.unwrap().body.code,
739
1
        vec![Instruction::Return(Some(
740
1
            crate::ir::LiteralOrSource::Literal(crate::ir::Literal::Integer(42))
741
1
        ))]
742
1
    );
743
1
}
744

            
745
1
#[test]
746
#[allow(clippy::too_many_lines)]
747
1
fn roundtrip_all_instructions() {
748
1
    // These instructions don't make any sense. We just need one each
749
1
    // instruction, and we need to ensure we try each
750
1
    // LiteralOrSource/Destination variant.
751
1
    let mut block = CodeBlockBuilder::default();
752
1
    block.push(Instruction::Return(None));
753
1
    let init = Function::new("__init", block.finish());
754
1

            
755
1
    let mut block = CodeBlockBuilder::default();
756
1
    let arg1 = block.new_argument("test");
757
1
    let var1 = block.variable_index_from_name(&Symbol::from("test"));
758
1
    let a_label = block.named_label("a_label");
759
1
    block.label(a_label.clone());
760
1
    block.push(Instruction::Add {
761
1
        left: LiteralOrSource::Argument(arg1),
762
1
        right: LiteralOrSource::Variable(var1.clone()),
763
1
        destination: Destination::Variable(var1.clone()),
764
1
    });
765
1
    block.push(Instruction::Sub {
766
1
        left: LiteralOrSource::Literal(Literal::Boolean(false)),
767
1
        right: LiteralOrSource::Literal(Literal::Boolean(true)),
768
1
        destination: Destination::Return,
769
1
    });
770
1
    block.push(Instruction::Multiply {
771
1
        left: LiteralOrSource::Literal(Literal::String(String::from("a string"))),
772
1
        right: LiteralOrSource::Literal(Literal::Real(1.0)),
773
1
        destination: Destination::Stack,
774
1
    });
775
1
    block.push(Instruction::Divide {
776
1
        left: LiteralOrSource::Stack,
777
1
        right: LiteralOrSource::Literal(Literal::Integer(0)),
778
1
        destination: Destination::Stack,
779
1
    });
780
1
    block.push(Instruction::LogicalAnd {
781
1
        left: LiteralOrSource::Literal(Literal::Void),
782
1
        right: LiteralOrSource::Literal(Literal::Void),
783
1
        destination: Destination::Stack,
784
1
    });
785
1
    block.push(Instruction::LogicalOr {
786
1
        left: LiteralOrSource::Literal(Literal::Void),
787
1
        right: LiteralOrSource::Literal(Literal::Void),
788
1
        destination: Destination::Stack,
789
1
    });
790
1
    block.push(Instruction::LogicalXor {
791
1
        left: LiteralOrSource::Literal(Literal::Void),
792
1
        right: LiteralOrSource::Literal(Literal::Void),
793
1
        destination: Destination::Stack,
794
1
    });
795
1
    block.push(Instruction::BitwiseAnd {
796
1
        left: LiteralOrSource::Literal(Literal::Void),
797
1
        right: LiteralOrSource::Literal(Literal::Void),
798
1
        destination: Destination::Stack,
799
1
    });
800
1
    block.push(Instruction::BitwiseOr {
801
1
        left: LiteralOrSource::Literal(Literal::Void),
802
1
        right: LiteralOrSource::Literal(Literal::Void),
803
1
        destination: Destination::Stack,
804
1
    });
805
1
    block.push(Instruction::BitwiseXor {
806
1
        left: LiteralOrSource::Literal(Literal::Void),
807
1
        right: LiteralOrSource::Literal(Literal::Void),
808
1
        destination: Destination::Stack,
809
1
    });
810
1
    block.push(Instruction::ShiftLeft {
811
1
        left: LiteralOrSource::Literal(Literal::Void),
812
1
        right: LiteralOrSource::Literal(Literal::Void),
813
1
        destination: Destination::Stack,
814
1
    });
815
1
    block.push(Instruction::ShiftRight {
816
1
        left: LiteralOrSource::Literal(Literal::Void),
817
1
        right: LiteralOrSource::Literal(Literal::Void),
818
1
        destination: Destination::Stack,
819
1
    });
820
1
    block.push(Instruction::LogicalNot {
821
1
        value: LiteralOrSource::Literal(Literal::Void),
822
1
        destination: Destination::Stack,
823
1
    });
824
1
    block.push(Instruction::BitwiseNot {
825
1
        value: LiteralOrSource::Literal(Literal::Void),
826
1
        destination: Destination::Stack,
827
1
    });
828
1
    block.push(Instruction::Convert {
829
1
        value: LiteralOrSource::Literal(Literal::Void),
830
1
        kind: ValueKind::Boolean,
831
1
        destination: Destination::Stack,
832
1
    });
833
1
    block.push(Instruction::Convert {
834
1
        value: LiteralOrSource::Literal(Literal::Void),
835
1
        kind: ValueKind::Integer,
836
1
        destination: Destination::Stack,
837
1
    });
838
1
    block.push(Instruction::Convert {
839
1
        value: LiteralOrSource::Literal(Literal::Void),
840
1
        kind: ValueKind::Real,
841
1
        destination: Destination::Stack,
842
1
    });
843
1
    block.push(Instruction::Convert {
844
1
        value: LiteralOrSource::Literal(Literal::Void),
845
1
        kind: ValueKind::Dynamic(Symbol::from("Dynamic")),
846
1
        destination: Destination::Stack,
847
1
    });
848
1
    block.push(Instruction::If {
849
1
        condition: LiteralOrSource::Literal(Literal::Void),
850
1
        false_jump_to: a_label.clone(),
851
1
    });
852
1
    block.push(Instruction::JumpTo(a_label.clone()));
853
1
    block.push(Instruction::Compare {
854
1
        comparison: Comparison::Equal,
855
1
        left: LiteralOrSource::Literal(Literal::Void),
856
1
        right: LiteralOrSource::Literal(Literal::Void),
857
1
        action: CompareAction::JumpIfFalse(a_label),
858
1
    });
859
1
    block.push(Instruction::Compare {
860
1
        comparison: Comparison::NotEqual,
861
1
        left: LiteralOrSource::Literal(Literal::Void),
862
1
        right: LiteralOrSource::Literal(Literal::Void),
863
1
        action: CompareAction::Store(Destination::Stack),
864
1
    });
865
1
    block.push(Instruction::Compare {
866
1
        comparison: Comparison::GreaterThan,
867
1
        left: LiteralOrSource::Literal(Literal::Void),
868
1
        right: LiteralOrSource::Literal(Literal::Void),
869
1
        action: CompareAction::Store(Destination::Stack),
870
1
    });
871
1
    block.push(Instruction::Compare {
872
1
        comparison: Comparison::GreaterThanOrEqual,
873
1
        left: LiteralOrSource::Literal(Literal::Void),
874
1
        right: LiteralOrSource::Literal(Literal::Void),
875
1
        action: CompareAction::Store(Destination::Stack),
876
1
    });
877
1
    block.push(Instruction::Compare {
878
1
        comparison: Comparison::LessThan,
879
1
        left: LiteralOrSource::Literal(Literal::Void),
880
1
        right: LiteralOrSource::Literal(Literal::Void),
881
1
        action: CompareAction::Store(Destination::Stack),
882
1
    });
883
1
    block.push(Instruction::Compare {
884
1
        comparison: Comparison::LessThanOrEqual,
885
1
        left: LiteralOrSource::Literal(Literal::Void),
886
1
        right: LiteralOrSource::Literal(Literal::Void),
887
1
        action: CompareAction::Store(Destination::Stack),
888
1
    });
889
1
    block.push(Instruction::Push(LiteralOrSource::Literal(Literal::Void)));
890
1
    block.push(Instruction::Load {
891
1
        value: LiteralOrSource::Literal(Literal::Void),
892
1
        variable: var1.clone(),
893
1
    });
894
1
    block.push(Instruction::Return(Some(LiteralOrSource::Literal(
895
1
        Literal::Void,
896
1
    ))));
897
1
    block.push(Instruction::Call {
898
1
        function: None,
899
1
        arg_count: 1,
900
1
        destination: Destination::Stack,
901
1
    });
902
1
    block.push(Instruction::Call {
903
1
        function: Some(Symbol::from("test")),
904
1
        arg_count: 1,
905
1
        destination: Destination::Stack,
906
1
    });
907
1
    block.push(Instruction::CallInstance {
908
1
        target: LiteralOrSource::Stack,
909
1
        name: Symbol::from("test"),
910
1
        arg_count: 1,
911
1
        destination: Destination::Stack,
912
1
    });
913
1
    block.push(Instruction::CallInstance {
914
1
        target: LiteralOrSource::Variable(var1),
915
1
        name: Symbol::from("test"),
916
1
        arg_count: 1,
917
1
        destination: Destination::Stack,
918
1
    });
919
1
    block.push(Instruction::CallIntrinsic {
920
1
        intrinsic: crate::Noop,
921
1
        arg_count: 1,
922
1
        destination: Destination::Stack,
923
1
    });
924
1
    let test_func = Function::new("test", block.finish());
925
1

            
926
1
    let manually_built = Module::new(vec![test_func], Vec::new(), Some(init));
927
1

            
928
1
    let assembly = manually_built.to_string();
929
1
    println!("Source:");
930
1
    println!("{assembly}");
931
1
    let parsed = Parser::parse(&assembly).unwrap();
932
1
    println!("Parsed source:");
933
1
    println!("{parsed}");
934
1
    assert_eq!(manually_built, parsed);
935
1
}