avra_lib/
directive.rs

1//! Contains assembler directive representation of AVRA-rs
2
3use strum_macros::{Display, EnumString};
4
5use crate::{
6    device::{Device, DEVICES},
7    expr::Expr,
8    parser::{
9        parse_file_internal, CodePoint, DataDefine, Item, NextItem, ParseContext, Segment,
10        SegmentType,
11    },
12};
13
14use crate::context::Context;
15use failure::{bail, Error};
16use std::path::PathBuf;
17
18#[derive(Clone, PartialEq, Eq, Debug, EnumString, Display)]
19#[strum(serialize_all = "lowercase")]
20pub enum Directive {
21    /// Reserve bytes for a variable in RAM
22    Byte,
23    /// Code segment
24    CSeg,
25    /// Program memory size for AT94K (unsupported)
26    CSegSize,
27    /// Define constant bytes in program memory and EEPROM
28    Db,
29    /// Define a symbolic name on a register
30    Def,
31    /// Device specific
32    Device,
33    /// Data segment
34    DSeg,
35    /// Define constant words in program memory and EEPROM
36    Dw,
37    /// End of macro define
38    EndM,
39    EndMacro,
40    /// Set a symbol equal to an expression
41    Equ,
42    /// EEPROM segment
43    ESeg,
44    /// Exit from file
45    Exit,
46    /// Read source from another file
47    Include,
48    /// Another path for includes
49    IncludePath,
50    /// Turn listfile generation on (unsupported)
51    List,
52    /// Turn macro expansion in list file on (unsupported)
53    ListMac,
54    /// Begin macro
55    Macro,
56    /// Turn listfile generation off (unsupported)
57    NoList,
58    /// Set program origin
59    Org,
60    /// Set a symbol to an expression
61    Set,
62    /// Define a preprocessor macro
63    Define,
64    /// Conditional assembly - alternate branches
65    Else,
66    ElIf,
67    /// Conditional assembly - end conditional block
68    Endif,
69    /// Outputs an error message
70    Error,
71    /// Conditional assembly - begin of conditional block
72    If,
73    IfDef,
74    IfNDef,
75    /// Outputs a message string
76    Message,
77    /// Define constant double words in program memory and EEPROM
78    Dd,
79    /// Define constant quad words in program memory and EEPROM
80    Dq,
81    /// Undefine register symbol
82    Undef,
83    /// Outputs a warning message
84    Warning,
85    /// Set up/down overlapping section (unsupported)
86    Overlap,
87    NoOverlap,
88    /// Preprocessor pragmas
89    Pragma,
90
91    /// For extend and macross support
92    #[strum(disabled)]
93    Custom(String),
94}
95
96impl Directive {
97    pub fn parse(
98        &self,
99        opts: &DirectiveOps,
100        context: &ParseContext,
101        point: CodePoint,
102    ) -> Result<NextItem, Error> {
103        let mut next_item = NextItem::NewLine;
104
105        let ParseContext {
106            current_path,
107            include_paths,
108            common_context,
109            segments,
110            macros,
111            messages,
112        } = context;
113
114        match self {
115            Directive::Db | Directive::Dw | Directive::Dd | Directive::Dq => {
116                if let DirectiveOps::OpList(args) = opts {
117                    let item_type = match self {
118                        Directive::Db => DataDefine::Db,
119                        Directive::Dw => DataDefine::Dw,
120                        Directive::Dd => DataDefine::Dd,
121                        Directive::Dq => DataDefine::Dq,
122                        _ => bail!("unknown argument for define data, {}", point),
123                    };
124                    context.push_to_last((point, Item::Data(item_type, args.clone())));
125                } else {
126                    bail!("Not allowed type of arguments for .db, {}", point);
127                }
128            }
129            Directive::Set | Directive::Def => {
130                if let DirectiveOps::Assign(Expr::Ident(name), expr) = opts {
131                    match self {
132                        Directive::Set => {
133                            context.push_to_last((point, Item::Set(name.clone(), expr.clone())))
134                        }
135                        Directive::Def => {
136                            context.push_to_last((point, Item::Def(name.clone(), expr.clone())))
137                        }
138                        _ => bail!("unknown argument for {} data, {}", self, point),
139                    };
140                } else {
141                    bail!("Not allowed type of arguments for .{}, {}", self, point);
142                }
143            }
144            Directive::Undef => {
145                if let DirectiveOps::OpList(values) = opts {
146                    if let Operand::E(Expr::Ident(name)) = &values[0] {
147                        context.push_to_last((point, Item::Undef(name.clone())))
148                    } else {
149                        bail!("Not allowed type of arguments for .{}, {}", self, point);
150                    }
151                } else {
152                    bail!("Not allowed type of arguments for .{}, {}", self, point);
153                }
154            }
155            Directive::Pragma => {
156                if let DirectiveOps::OpList(args) = opts {
157                    context.push_to_last((point, Item::Pragma(args.clone())));
158                } else {
159                    bail!("Not allowed type of arguments for .{}, {}", self, point);
160                }
161            }
162            Directive::Byte => {
163                if let DirectiveOps::OpList(args) = opts {
164                    if args.len() > 1 {
165                        bail!("Too many arguments for {}", self);
166                    }
167                    if let Operand::E(expr) = &args[0] {
168                        if let Expr::Const(n) = expr {
169                            context.push_to_last((point, Item::ReserveData(*n)));
170                        }
171                    }
172                } else {
173                    bail!("Not allowed type of arguments for .byte, {}", point);
174                }
175            }
176            Directive::Equ => {
177                if let DirectiveOps::Assign(name, value) = opts {
178                    if let Expr::Ident(name) = name {
179                        context.common_context.set_equ(name.clone(), value.clone());
180                    }
181                } else {
182                    bail!("wrong format for .equ, expected: {} in {}", opts, point,);
183                }
184            }
185            Directive::Org => {
186                if let DirectiveOps::OpList(values) = opts {
187                    if let Operand::E(Expr::Const(value)) = &values[0] {
188                        if !context.last_segment().unwrap().borrow().is_empty() {
189                            let current_type = context.last_segment().unwrap().borrow().t;
190                            context.add_segment(Segment::new(current_type));
191                        }
192                        context.last_segment().unwrap().borrow_mut().address = *value as u32;
193                    }
194                } else {
195                    bail!("wrong format for .org, expected: {} in {}", opts, point,);
196                }
197            }
198            Directive::CSeg | Directive::DSeg | Directive::ESeg => {
199                let new_type = match self {
200                    Directive::CSeg => SegmentType::Code,
201                    Directive::DSeg => SegmentType::Data,
202                    Directive::ESeg => SegmentType::Eeprom,
203                    _ => SegmentType::Code,
204                };
205
206                if !context.last_segment().unwrap().borrow().is_empty() {
207                    context.add_segment(Segment::new(new_type));
208                } else {
209                    context.last_segment().unwrap().borrow_mut().t = new_type;
210                }
211            }
212            Directive::Device => {
213                if let DirectiveOps::OpList(values) = opts {
214                    if let Operand::E(Expr::Ident(value)) = &values[0] {
215                        if let Some(device) = DEVICES.get(value.as_str()) {
216                            if let Some(old_device) = context
217                                .common_context
218                                .device
219                                .as_ref()
220                                .clone()
221                                .borrow()
222                                .as_ref()
223                            {
224                                if old_device == &Device::new(0) {
225                                    context.common_context.device.replace(Some(device.clone()));
226                                } else {
227                                    bail!(
228                                        "device redefinition in {}, old: {:?} -> new: {:?}",
229                                        point,
230                                        old_device,
231                                        device,
232                                    )
233                                }
234                            } else {
235                                context.common_context.device.replace(Some(device.clone()));
236                            }
237                        } else {
238                            bail!("unknown device {} in {}", value, point,)
239                        }
240                    }
241                } else {
242                    bail!("wrong format for .device, expected: {} in {}", opts, point,);
243                }
244            }
245            Directive::Include => {
246                if let DirectiveOps::OpList(values) = &opts {
247                    if let Operand::S(include) = &values[0] {
248                        let context = ParseContext {
249                            current_path: PathBuf::from(include),
250                            include_paths: include_paths.clone(),
251                            common_context: common_context.clone(),
252                            segments: segments.clone(),
253                            macros: macros.clone(),
254                            messages: messages.clone(),
255                        };
256                        parse_file_internal(&context)?;
257                    } else {
258                        bail!("wrong format for .include, expected: {} in {}", opts, point,);
259                    }
260                } else {
261                    bail!("wrong format for .include, expected: {} in {}", opts, point,);
262                }
263            }
264            Directive::IncludePath => {
265                if let DirectiveOps::OpList(values) = &opts {
266                    if let Operand::S(include) = &values[0] {
267                        let path = PathBuf::from(include);
268                        let path = if path.is_relative() {
269                            let mut current_path = current_path.parent().unwrap().to_path_buf();
270                            current_path.push(path);
271                            current_path
272                        } else {
273                            path
274                        };
275                        if !include_paths.borrow_mut().contains(&path) {
276                            include_paths.borrow_mut().insert(path);
277                        }
278                    } else {
279                        bail!(
280                            "wrong format for .includepath, expected: {} in {}",
281                            opts,
282                            point,
283                        );
284                    }
285                } else {
286                    bail!(
287                        "wrong format for .includepath, expected: {} in {}",
288                        opts,
289                        point,
290                    );
291                }
292            }
293            Directive::If | Directive::ElIf => {
294                if let DirectiveOps::OpList(values) = &opts {
295                    if let Operand::E(expr) = &values[0] {
296                        let value = expr.run(&context.common_context)?;
297                        if value == 0 {
298                            next_item = NextItem::EndIf;
299                        }
300                    } else {
301                        bail!(
302                            "wrong format for .{}, expected: {} in {}",
303                            self,
304                            opts,
305                            point,
306                        );
307                    }
308                } else {
309                    bail!(
310                        "wrong format for .{}, expected: {} in {}",
311                        self,
312                        opts,
313                        point,
314                    );
315                }
316            }
317            Directive::IfNDef | Directive::IfDef => {
318                if let DirectiveOps::OpList(values) = &opts {
319                    if let Operand::E(Expr::Ident(name)) = &values[0] {
320                        if context.common_context.defines.borrow().contains_key(name) {
321                            if self == &Directive::IfNDef {
322                                next_item = NextItem::EndIf;
323                            };
324                        } else {
325                            if self == &Directive::IfDef {
326                                next_item = NextItem::EndIf;
327                            }
328                        }
329                    } else {
330                        bail!(
331                            "wrong format for .{}, expected: {} in {}",
332                            self,
333                            opts,
334                            point,
335                        );
336                    }
337                } else {
338                    bail!(
339                        "wrong format for .{}, expected: {} in {}",
340                        self,
341                        opts,
342                        point,
343                    );
344                }
345            }
346            Directive::Define => {
347                if let DirectiveOps::OpList(values) = &opts {
348                    if let Operand::E(Expr::Ident(name)) = &values[0] {
349                        context
350                            .common_context
351                            .set_define(name.clone(), Expr::Const(0));
352                    } else {
353                        bail!("wrong format for .define, expected: {} in {}", opts, point,);
354                    }
355                } else {
356                    bail!("wrong format for .define, expected: {} in {}", opts, point,);
357                }
358            }
359            Directive::Else => {
360                next_item = NextItem::EndIf;
361            }
362            Directive::Endif => {}
363            Directive::Exit => {
364                next_item = NextItem::EndFile;
365            }
366            Directive::Macro => {
367                if let DirectiveOps::OpList(values) = &opts {
368                    if let Operand::E(Expr::Ident(name)) = &values[0] {
369                        context.macros.name.replace(name.clone());
370                        next_item = NextItem::EndMacro;
371                    } else {
372                        bail!("wrong format for .macro, expected: {} in {}", opts, point,);
373                    }
374                } else {
375                    bail!("wrong format for .macro, expected: {} in {}", opts, point,);
376                }
377            }
378            // Skip because we not need any actions for ATtiny and ATmega families
379            Directive::CSegSize => {}
380            Directive::Message | Directive::Warning | Directive::Error => {
381                if let DirectiveOps::OpList(values) = &opts {
382                    if let Operand::S(message) = &values[0] {
383                        let message_type = match self {
384                            Directive::Message => "info",
385                            Directive::Warning => "warning",
386                            Directive::Error => "error",
387                            _ => bail!("wrong option for message in {}", point),
388                        };
389                        context
390                            .messages
391                            .borrow_mut()
392                            .push(format!("{}: {} in {}", message_type, message, point));
393                        if self == &Directive::Error {
394                            bail!("failed by error: {} in {}", message, point);
395                        }
396                    } else {
397                        bail!(
398                            "wrong format for .{}, expected: {} in {}",
399                            self,
400                            opts,
401                            point,
402                        );
403                    }
404                } else {
405                    bail!(
406                        "wrong format for .{}, expected: {} in {}",
407                        self,
408                        opts,
409                        point,
410                    );
411                }
412            }
413            Directive::Custom(name) => bail!("unsupported custom directive {}, {}", name, point),
414            _ => bail!(
415                "Unsupported directive {} in {} segment, {}",
416                self,
417                context.last_segment().unwrap().borrow().t,
418                point,
419            ),
420        }
421
422        Ok(next_item)
423    }
424}
425
426#[derive(Clone, PartialEq, Eq, Debug)]
427pub enum Operand {
428    E(Expr),
429    S(String),
430}
431
432impl Operand {
433    pub fn len(&self) -> usize {
434        match self {
435            Operand::E(_) => 1,
436            Operand::S(s) => s.len(),
437        }
438    }
439
440    pub fn get_bytes(&self, e_p: &dyn Context) -> Result<Vec<u8>, Error> {
441        match self {
442            Operand::E(expr) => Ok(vec![expr.get_byte(e_p)?]),
443            Operand::S(s) => Ok(s.as_bytes().to_vec()),
444        }
445    }
446
447    pub fn get_words(&self, e_p: &dyn Context) -> Result<Vec<u8>, Error> {
448        match self {
449            Operand::E(expr) => Ok(expr.get_words(e_p)?.to_vec()),
450            Operand::S(_) => bail!("not allowed to convert string as 2 bytes array"),
451        }
452    }
453
454    pub fn get_double_words(&self, e_p: &dyn Context) -> Result<Vec<u8>, Error> {
455        match self {
456            Operand::E(expr) => Ok(expr.get_double_words(e_p)?.to_vec()),
457            Operand::S(_) => bail!("not allowed to convert string as 2 bytes array"),
458        }
459    }
460
461    pub fn get_quad_words(&self, e_p: &dyn Context) -> Result<Vec<u8>, Error> {
462        match self {
463            Operand::E(expr) => Ok(expr.get_quad_words(e_p)?.to_vec()),
464            Operand::S(_) => bail!("not allowed to convert string as 2 bytes array"),
465        }
466    }
467}
468
469#[derive(Clone, PartialEq, Eq, Debug, Display)]
470pub enum DirectiveOps {
471    OpList(Vec<Operand>),
472    Assign(Expr, Expr),
473}
474
475pub trait GetData {
476    fn actual_len(&self) -> usize;
477    fn get_bytes(&self, constants: &dyn Context) -> Result<Vec<u8>, Error>;
478    fn get_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error>;
479    fn get_double_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error>;
480    fn get_quad_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error>;
481}
482
483impl GetData for Vec<Operand> {
484    fn actual_len(&self) -> usize {
485        self.iter().fold(0, |acc, op| acc + op.len())
486    }
487
488    fn get_bytes(&self, constants: &dyn Context) -> Result<Vec<u8>, Error> {
489        let mut bytes = vec![];
490        for item in self {
491            bytes.extend(item.get_bytes(constants)?);
492        }
493
494        Ok(bytes)
495    }
496    fn get_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error> {
497        let mut bytes = vec![];
498        for item in self {
499            bytes.extend(item.get_words(constants)?);
500        }
501
502        Ok(bytes)
503    }
504    fn get_double_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error> {
505        let mut bytes = vec![];
506        for item in self {
507            bytes.extend(item.get_double_words(constants)?);
508        }
509
510        Ok(bytes)
511    }
512    fn get_quad_words(&self, constants: &dyn Context) -> Result<Vec<u8>, Error> {
513        let mut bytes = vec![];
514        for item in self {
515            bytes.extend(item.get_quad_words(constants)?);
516        }
517
518        Ok(bytes)
519    }
520}
521
522#[cfg(test)]
523mod parser_tests {
524    use super::*;
525
526    use crate::{
527        device::DEVICES,
528        directive::Operand,
529        document::{document, Document},
530        expr::{BinaryExpr, BinaryOperator},
531        instruction::{
532            operation::Operation,
533            register::{Reg16, Reg8},
534            IndexOps, InstructionOps,
535        },
536        parser::{parse_file, parse_str, ParseResult},
537    };
538
539    use crate::context::CommonContext;
540    use maplit::{btreeset, hashmap};
541    use std::path::PathBuf;
542
543    #[test]
544    fn directive_test() {
545        assert_eq!(document::directive(".equ"), Ok(Directive::Equ));
546
547        assert_eq!(document::directive(".dseg"), Ok(Directive::DSeg));
548    }
549
550    #[test]
551    fn directive_op_test() {
552        assert_eq!(
553            document::directive_op("a"),
554            Ok(Operand::E(Expr::Ident("a".to_string())))
555        );
556
557        assert_eq!(
558            document::directive_op("\"bla bla bla\""),
559            Ok(Operand::S("bla bla bla".to_string()))
560        );
561    }
562
563    #[test]
564    fn directive_ops_test() {
565        assert_eq!(
566            document::directive_ops(""),
567            Ok(DirectiveOps::OpList(vec![]))
568        );
569
570        assert_eq!(
571            document::directive_ops("a\t, b,c  ,\td"),
572            Ok(DirectiveOps::OpList(vec![
573                Operand::E(Expr::Ident("a".to_string())),
574                Operand::E(Expr::Ident("b".to_string())),
575                Operand::E(Expr::Ident("c".to_string())),
576                Operand::E(Expr::Ident("d".to_string()))
577            ]))
578        );
579
580        assert_eq!(
581            document::directive_ops("a,b,c,d"),
582            Ok(DirectiveOps::OpList(vec![
583                Operand::E(Expr::Ident("a".to_string())),
584                Operand::E(Expr::Ident("b".to_string())),
585                Operand::E(Expr::Ident("c".to_string())),
586                Operand::E(Expr::Ident("d".to_string()))
587            ]))
588        );
589
590        assert_eq!(
591            document::directive_ops("a b c d"),
592            Ok(DirectiveOps::OpList(vec![
593                Operand::E(Expr::Ident("a".to_string())),
594                Operand::E(Expr::Ident("b".to_string())),
595                Operand::E(Expr::Ident("c".to_string())),
596                Operand::E(Expr::Ident("d".to_string()))
597            ]))
598        );
599
600        assert_eq!(
601            document::directive_ops("t = 44"),
602            Ok(DirectiveOps::Assign(
603                Expr::Ident("t".to_string()),
604                Expr::Const(44)
605            ))
606        );
607    }
608
609    #[test]
610    fn directive_line_test() {
611        assert_eq!(
612            document::directive_line(".dseg"),
613            Ok(Document::DirectiveLine(
614                Box::new(None),
615                Directive::DSeg,
616                DirectiveOps::OpList(vec![])
617            ))
618        );
619
620        assert_eq!(
621            document::directive_line(".equ Last = 8"),
622            Ok(Document::DirectiveLine(
623                Box::new(None),
624                Directive::Equ,
625                DirectiveOps::Assign(Expr::Ident("Last".to_string()), Expr::Const(8))
626            ))
627        );
628
629        assert_eq!(
630            document::directive_line("#define MIDDLE"),
631            Ok(Document::DirectiveLine(
632                Box::new(None),
633                Directive::Define,
634                DirectiveOps::OpList(vec![Operand::E(Expr::Ident("MIDDLE".to_string()))])
635            ))
636        );
637    }
638
639    #[test]
640    fn check_org() {
641        let common_context = CommonContext::new();
642        let parse_result = parse_str("good_point:\n.org 0x2\ngood_point2:", &common_context);
643        assert_eq!(
644            parse_result.unwrap(),
645            ParseResult {
646                segments: vec![
647                    Segment {
648                        items: vec![(
649                            CodePoint {
650                                line_num: 1,
651                                num: 1
652                            },
653                            Item::Label("good_point".to_string())
654                        )],
655                        t: SegmentType::Code,
656                        address: 0
657                    },
658                    Segment {
659                        items: vec![(
660                            CodePoint {
661                                line_num: 3,
662                                num: 1
663                            },
664                            Item::Label("good_point2".to_string())
665                        )],
666                        t: SegmentType::Code,
667                        address: 0x2
668                    }
669                ],
670                macroses: hashmap! {},
671                messages: vec![],
672            }
673        );
674    }
675
676    #[test]
677    fn check_segments() {
678        let common_context = CommonContext::new();
679        let parse_result = parse_str("good_point:\n.cseg\ngood_point2:", &common_context);
680        assert_eq!(
681            parse_result.unwrap(),
682            ParseResult {
683                segments: vec![
684                    Segment {
685                        items: vec![(
686                            CodePoint {
687                                line_num: 1,
688                                num: 1
689                            },
690                            Item::Label("good_point".to_string())
691                        )],
692                        t: SegmentType::Code,
693                        address: 0
694                    },
695                    Segment {
696                        items: vec![(
697                            CodePoint {
698                                line_num: 3,
699                                num: 1
700                            },
701                            Item::Label("good_point2".to_string())
702                        )],
703                        t: SegmentType::Code,
704                        address: 0
705                    }
706                ],
707                macroses: hashmap! {},
708                messages: vec![],
709            }
710        );
711
712        let common_context = CommonContext::new();
713        let parse_result = parse_str(
714            "good_point:\n.cseg\n.org 0x20\ngood_point2:",
715            &common_context,
716        );
717        assert_eq!(
718            parse_result.unwrap(),
719            ParseResult {
720                segments: vec![
721                    Segment {
722                        items: vec![(
723                            CodePoint {
724                                line_num: 1,
725                                num: 1
726                            },
727                            Item::Label("good_point".to_string())
728                        )],
729                        t: SegmentType::Code,
730                        address: 0
731                    },
732                    Segment {
733                        items: vec![(
734                            CodePoint {
735                                line_num: 4,
736                                num: 1
737                            },
738                            Item::Label("good_point2".to_string())
739                        )],
740                        t: SegmentType::Code,
741                        address: 0x20
742                    }
743                ],
744                macroses: hashmap! {},
745                messages: vec![],
746            }
747        );
748
749        let common_context = CommonContext::new();
750        let parse_result = parse_str(
751            "good_point:\n.dseg\ngood_point2:\n.cseg\ngood_point3:",
752            &common_context,
753        );
754        assert_eq!(
755            parse_result.unwrap(),
756            ParseResult {
757                segments: vec![
758                    Segment {
759                        items: vec![(
760                            CodePoint {
761                                line_num: 1,
762                                num: 1
763                            },
764                            Item::Label("good_point".to_string())
765                        )],
766                        t: SegmentType::Code,
767                        address: 0
768                    },
769                    Segment {
770                        items: vec![(
771                            CodePoint {
772                                line_num: 3,
773                                num: 1
774                            },
775                            Item::Label("good_point2".to_string())
776                        )],
777                        t: SegmentType::Data,
778                        address: 0
779                    },
780                    Segment {
781                        items: vec![(
782                            CodePoint {
783                                line_num: 5,
784                                num: 1
785                            },
786                            Item::Label("good_point3".to_string())
787                        )],
788                        t: SegmentType::Code,
789                        address: 0
790                    },
791                ],
792                macroses: hashmap! {},
793                messages: vec![],
794            }
795        );
796    }
797
798    #[test]
799    fn check_db_dw_dd_dq() {
800        let common_context = CommonContext::new();
801        let parse_result = parse_str(
802            "ldi r16, data\ndata:\n.db 15, 26, \"Hello, World\", end",
803            &common_context,
804        );
805        assert_eq!(
806            parse_result.unwrap(),
807            ParseResult {
808                segments: vec![Segment {
809                    items: vec![
810                        (
811                            CodePoint {
812                                line_num: 1,
813                                num: 2
814                            },
815                            Item::Instruction(
816                                Operation::Ldi,
817                                vec![
818                                    InstructionOps::R8(Reg8::R16),
819                                    InstructionOps::E(Expr::Ident("data".to_string()))
820                                ]
821                            )
822                        ),
823                        (
824                            CodePoint {
825                                line_num: 2,
826                                num: 1
827                            },
828                            Item::Label("data".to_string())
829                        ),
830                        (
831                            CodePoint {
832                                line_num: 3,
833                                num: 2
834                            },
835                            Item::Data(
836                                DataDefine::Db,
837                                vec![
838                                    Operand::E(Expr::Const(15)),
839                                    Operand::E(Expr::Const(26)),
840                                    Operand::S("Hello, World".to_string()),
841                                    Operand::E(Expr::Ident("end".to_string())),
842                                ]
843                            )
844                        )
845                    ],
846                    t: SegmentType::Code,
847                    address: 0
848                }],
849                macroses: hashmap! {},
850                messages: vec![],
851            }
852        );
853
854        let common_context = CommonContext::new();
855        let parse_result = parse_str(
856            "ldi r18, data_w\ndata_w:\n.dw 0xff44, end, 0xda4e",
857            &common_context,
858        );
859        assert_eq!(
860            parse_result.unwrap(),
861            ParseResult {
862                segments: vec![Segment {
863                    items: vec![
864                        (
865                            CodePoint {
866                                line_num: 1,
867                                num: 2
868                            },
869                            Item::Instruction(
870                                Operation::Ldi,
871                                vec![
872                                    InstructionOps::R8(Reg8::R18),
873                                    InstructionOps::E(Expr::Ident("data_w".to_string()))
874                                ]
875                            )
876                        ),
877                        (
878                            CodePoint {
879                                line_num: 2,
880                                num: 1
881                            },
882                            Item::Label("data_w".to_string())
883                        ),
884                        (
885                            CodePoint {
886                                line_num: 3,
887                                num: 2
888                            },
889                            Item::Data(
890                                DataDefine::Dw,
891                                vec![
892                                    Operand::E(Expr::Const(0xff44)),
893                                    Operand::E(Expr::Ident("end".to_string())),
894                                    Operand::E(Expr::Const(0xda4e))
895                                ]
896                            )
897                        )
898                    ],
899                    t: SegmentType::Code,
900                    address: 0
901                }],
902                macroses: hashmap! {},
903                messages: vec![],
904            }
905        );
906
907        let common_context = CommonContext::new();
908        let parse_result = parse_str(
909            "ldi r18, data_w\ndata_w:\n.dw 0xff44, end, 0xda4e",
910            &common_context,
911        );
912        assert_eq!(
913            parse_result.unwrap(),
914            ParseResult {
915                segments: vec![Segment {
916                    items: vec![
917                        (
918                            CodePoint {
919                                line_num: 1,
920                                num: 2
921                            },
922                            Item::Instruction(
923                                Operation::Ldi,
924                                vec![
925                                    InstructionOps::R8(Reg8::R18),
926                                    InstructionOps::E(Expr::Ident("data_w".to_string()))
927                                ]
928                            )
929                        ),
930                        (
931                            CodePoint {
932                                line_num: 2,
933                                num: 1
934                            },
935                            Item::Label("data_w".to_string())
936                        ),
937                        (
938                            CodePoint {
939                                line_num: 3,
940                                num: 2
941                            },
942                            Item::Data(
943                                DataDefine::Dw,
944                                vec![
945                                    Operand::E(Expr::Const(0xff44)),
946                                    Operand::E(Expr::Ident("end".to_string())),
947                                    Operand::E(Expr::Const(0xda4e))
948                                ]
949                            )
950                        )
951                    ],
952                    t: SegmentType::Code,
953                    address: 0
954                }],
955                macroses: hashmap! {},
956                messages: vec![],
957            }
958        );
959
960        let common_context = CommonContext::new();
961        let parse_result = parse_str(
962            "ldi r18, data_w\ndata_w:\n.dd 0xff44, end, 0xda4e",
963            &common_context,
964        );
965        assert_eq!(
966            parse_result.unwrap(),
967            ParseResult {
968                segments: vec![Segment {
969                    items: vec![
970                        (
971                            CodePoint {
972                                line_num: 1,
973                                num: 2
974                            },
975                            Item::Instruction(
976                                Operation::Ldi,
977                                vec![
978                                    InstructionOps::R8(Reg8::R18),
979                                    InstructionOps::E(Expr::Ident("data_w".to_string()))
980                                ]
981                            )
982                        ),
983                        (
984                            CodePoint {
985                                line_num: 2,
986                                num: 1
987                            },
988                            Item::Label("data_w".to_string())
989                        ),
990                        (
991                            CodePoint {
992                                line_num: 3,
993                                num: 2
994                            },
995                            Item::Data(
996                                DataDefine::Dd,
997                                vec![
998                                    Operand::E(Expr::Const(0xff44)),
999                                    Operand::E(Expr::Ident("end".to_string())),
1000                                    Operand::E(Expr::Const(0xda4e))
1001                                ]
1002                            )
1003                        )
1004                    ],
1005                    t: SegmentType::Code,
1006                    address: 0
1007                }],
1008                macroses: hashmap! {},
1009                messages: vec![],
1010            }
1011        );
1012
1013        let common_context = CommonContext::new();
1014        let parse_result = parse_str(
1015            "ldi r18, data_w\ndata_w:\n.dq 0xff44, end, 0xda4e",
1016            &common_context,
1017        );
1018        assert_eq!(
1019            parse_result.unwrap(),
1020            ParseResult {
1021                segments: vec![Segment {
1022                    items: vec![
1023                        (
1024                            CodePoint {
1025                                line_num: 1,
1026                                num: 2
1027                            },
1028                            Item::Instruction(
1029                                Operation::Ldi,
1030                                vec![
1031                                    InstructionOps::R8(Reg8::R18),
1032                                    InstructionOps::E(Expr::Ident("data_w".to_string()))
1033                                ]
1034                            )
1035                        ),
1036                        (
1037                            CodePoint {
1038                                line_num: 2,
1039                                num: 1
1040                            },
1041                            Item::Label("data_w".to_string())
1042                        ),
1043                        (
1044                            CodePoint {
1045                                line_num: 3,
1046                                num: 2
1047                            },
1048                            Item::Data(
1049                                DataDefine::Dq,
1050                                vec![
1051                                    Operand::E(Expr::Const(0xff44)),
1052                                    Operand::E(Expr::Ident("end".to_string())),
1053                                    Operand::E(Expr::Const(0xda4e))
1054                                ]
1055                            )
1056                        )
1057                    ],
1058                    t: SegmentType::Code,
1059                    address: 0
1060                }],
1061                macroses: hashmap! {},
1062                messages: vec![],
1063            }
1064        );
1065    }
1066
1067    #[test]
1068    fn check_directive_equ() {
1069        let common_context = CommonContext::new();
1070        let parse_result = parse_str(
1071            ".equ REG0 = 0x44\n.equ REG1 = 0x45\n.equ REG2 = 0x46",
1072            &common_context,
1073        );
1074        assert!(parse_result.is_ok());
1075
1076        assert_eq!(
1077            common_context.equs.borrow().clone(),
1078            hashmap! {
1079                        "reg0".to_string() => Expr::Const(0x44),
1080                        "reg1".to_string() => Expr::Const(0x45),
1081                        "reg2".to_string() => Expr::Const(0x46),
1082            }
1083        );
1084    }
1085
1086    #[test]
1087    fn check_directive_set() {
1088        let common_context = CommonContext::new();
1089        let parse_result = parse_str(".set t = 4\n.set t = t + 1", &common_context);
1090        assert_eq!(
1091            parse_result.unwrap(),
1092            ParseResult {
1093                segments: vec![Segment {
1094                    items: vec![
1095                        (
1096                            CodePoint {
1097                                line_num: 1,
1098                                num: 2
1099                            },
1100                            Item::Set("t".to_string(), Expr::Const(4))
1101                        ),
1102                        (
1103                            CodePoint {
1104                                line_num: 2,
1105                                num: 2
1106                            },
1107                            Item::Set(
1108                                "t".to_string(),
1109                                Expr::Binary(Box::new(BinaryExpr {
1110                                    left: Expr::Ident("t".to_string()),
1111                                    operator: BinaryOperator::Add,
1112                                    right: Expr::Const(1)
1113                                }))
1114                            )
1115                        )
1116                    ],
1117                    t: SegmentType::Code,
1118                    address: 0,
1119                }],
1120                macroses: hashmap! {},
1121                messages: vec![],
1122            }
1123        );
1124    }
1125
1126    #[test]
1127    fn check_directive_byte() {
1128        let common_context = CommonContext::new();
1129        let parse_result = parse_str(".dseg\ndata: .byte 1\ncounter: .byte 2", &common_context);
1130        assert_eq!(
1131            parse_result.unwrap(),
1132            ParseResult {
1133                segments: vec![Segment {
1134                    items: vec![
1135                        (
1136                            CodePoint {
1137                                line_num: 2,
1138                                num: 1
1139                            },
1140                            Item::Label("data".to_string())
1141                        ),
1142                        (
1143                            CodePoint {
1144                                line_num: 2,
1145                                num: 2
1146                            },
1147                            Item::ReserveData(1)
1148                        ),
1149                        (
1150                            CodePoint {
1151                                line_num: 3,
1152                                num: 1
1153                            },
1154                            Item::Label("counter".to_string())
1155                        ),
1156                        (
1157                            CodePoint {
1158                                line_num: 3,
1159                                num: 2
1160                            },
1161                            Item::ReserveData(2),
1162                        ),
1163                    ],
1164                    t: SegmentType::Data,
1165                    address: 0,
1166                }],
1167                macroses: hashmap! {},
1168                messages: vec![],
1169            }
1170        );
1171    }
1172
1173    #[test]
1174    fn check_directive_device() {
1175        let common_context = CommonContext::new();
1176        let parse_result = parse_str(".device ATmega48\nldd r25, Z+2", &common_context);
1177        assert_eq!(
1178            parse_result.unwrap(),
1179            ParseResult {
1180                segments: vec![Segment {
1181                    items: vec![(
1182                        CodePoint {
1183                            line_num: 2,
1184                            num: 2
1185                        },
1186                        Item::Instruction(
1187                            Operation::Ldd,
1188                            vec![
1189                                InstructionOps::R8(Reg8::R25),
1190                                InstructionOps::Index(IndexOps::PostIncrementE(
1191                                    Reg16::Z,
1192                                    Expr::Const(2)
1193                                ))
1194                            ]
1195                        )
1196                    ),],
1197                    t: SegmentType::Code,
1198                    address: 0
1199                }],
1200                macroses: hashmap! {},
1201                messages: vec![],
1202            }
1203        );
1204
1205        assert_eq!(
1206            common_context.get_device(),
1207            DEVICES.get("ATmega48").unwrap().clone()
1208        );
1209
1210        let common_context = CommonContext::new();
1211        let parse_result = parse_str(".device ATmega96\nldd r25, Z+2", &common_context);
1212        assert!(parse_result.is_err());
1213    }
1214
1215    #[test]
1216    fn check_directive_include() {
1217        let common_context = CommonContext::new();
1218        let parse_result = parse_file(
1219            PathBuf::from("tests/include_test.asm"),
1220            btreeset! {},
1221            &common_context,
1222        );
1223
1224        assert_eq!(
1225            parse_result.unwrap(),
1226            ParseResult {
1227                segments: vec![Segment {
1228                    items: vec![(
1229                        CodePoint {
1230                            line_num: 3,
1231                            num: 2
1232                        },
1233                        Item::Instruction(
1234                            Operation::In,
1235                            vec![
1236                                InstructionOps::R8(Reg8::R0),
1237                                InstructionOps::E(Expr::Ident("SREG".to_string()))
1238                            ]
1239                        )
1240                    ),],
1241                    t: SegmentType::Code,
1242                    address: 0
1243                }],
1244                macroses: hashmap! {},
1245                messages: vec![],
1246            }
1247        );
1248
1249        assert_eq!(
1250            common_context.equs.borrow().clone(),
1251            hashmap! {
1252                "sreg".to_string() => Expr::Const(0x3f),
1253            }
1254        );
1255
1256        assert_eq!(
1257            common_context.get_device(),
1258            DEVICES.get("ATmega48").unwrap().clone()
1259        );
1260    }
1261
1262    #[test]
1263    fn check_directive_include_path() {
1264        let common_context = CommonContext::new();
1265        let parse_result = parse_file(
1266            PathBuf::from("tests/include_path_test.asm"),
1267            btreeset! {},
1268            &common_context,
1269        );
1270
1271        assert_eq!(
1272            parse_result.unwrap(),
1273            ParseResult {
1274                segments: vec![Segment {
1275                    items: vec![(
1276                        CodePoint {
1277                            line_num: 4,
1278                            num: 2
1279                        },
1280                        Item::Instruction(
1281                            Operation::In,
1282                            vec![
1283                                InstructionOps::R8(Reg8::R0),
1284                                InstructionOps::E(Expr::Ident("SREG".to_string()))
1285                            ]
1286                        )
1287                    ),],
1288                    t: SegmentType::Code,
1289                    address: 0
1290                }],
1291                macroses: hashmap! {},
1292                messages: vec![],
1293            }
1294        );
1295
1296        assert_eq!(
1297            common_context.equs.borrow().clone(),
1298            hashmap! {
1299                "sreg".to_string() => Expr::Const(0x3f),
1300            }
1301        );
1302
1303        assert_eq!(
1304            common_context.get_device(),
1305            DEVICES.get("ATmega88").unwrap().clone()
1306        );
1307    }
1308
1309    #[test]
1310    fn check_directive_if_ifdef_ifndef_elif_else_define() {
1311        let common_context = CommonContext::new();
1312        let parse_result = parse_str(".define T\n.ifndef T\n.endif", &common_context);
1313        assert!(parse_result.is_ok());
1314
1315        assert_eq!(
1316            common_context.defines.borrow().clone(),
1317            hashmap! { "T".to_string() => Expr::Const(0) }
1318        );
1319
1320        let common_context = CommonContext::new();
1321        let parse_result = parse_str("\n.ifndef T\n.endif", &common_context);
1322        assert!(parse_result.is_ok());
1323
1324        let common_context = CommonContext::new();
1325        let parse_result = parse_str(".ifdef T\n.endif", &common_context);
1326        assert!(parse_result.is_ok());
1327
1328        let common_context = CommonContext::new();
1329        let parse_result = parse_str(".define T\n.ifdef T\n.endif", &common_context);
1330        assert!(parse_result.is_ok());
1331
1332        assert_eq!(
1333            common_context.defines.borrow().clone(),
1334            hashmap! { "T".to_string() => Expr::Const(0) }
1335        );
1336
1337        let common_context = CommonContext::new();
1338        let parse_result = parse_str(
1339            "\n.ifndef T\n.ifdef T\n.define T\n.endif\n.define X\n.endif",
1340            &common_context,
1341        );
1342        assert!(parse_result.is_ok());
1343
1344        assert_eq!(
1345            common_context.defines.borrow().clone(),
1346            hashmap! { "X".to_string() => Expr::Const(0) }
1347        );
1348
1349        let common_context = CommonContext::new();
1350        let parse_result = parse_str(
1351            ".ifdef T\n.define X\n.else\n.define Y\n.endif\n.define Z",
1352            &common_context,
1353        );
1354        assert!(parse_result.is_ok());
1355
1356        assert_eq!(
1357            common_context.defines.borrow().clone(),
1358            hashmap! { "Y".to_string() => Expr::Const(0), "Z".to_string() => Expr::Const(0) }
1359        );
1360
1361        let common_context = CommonContext::new();
1362        let parse_result = parse_str(
1363            ".ifndef T\n.define X\n.else\n.define Y\n.endif\n.define Z",
1364            &common_context,
1365        );
1366        assert!(parse_result.is_ok());
1367
1368        assert_eq!(
1369            common_context.defines.borrow().clone(),
1370            hashmap! { "X".to_string() => Expr::Const(0), "Z".to_string() => Expr::Const(0) }
1371        );
1372
1373        let common_context = CommonContext::new();
1374        let parse_result = parse_str(
1375            ".ifndef T\n.define X\n.else\n.endif\n.define Z",
1376            &common_context,
1377        );
1378        assert!(parse_result.is_ok());
1379
1380        assert_eq!(
1381            common_context.defines.borrow().clone(),
1382            hashmap! { "X".to_string() => Expr::Const(0), "Z".to_string() => Expr::Const(0) }
1383        );
1384
1385        let common_context = CommonContext::new();
1386        let parse_result = parse_str(
1387            ".if 4 > 5\n.define X\n.else\n.define Y\n.endif\n.define Z",
1388            &common_context,
1389        );
1390        assert!(parse_result.is_ok());
1391
1392        assert_eq!(
1393            common_context.defines.borrow().clone(),
1394            hashmap! { "Y".to_string() => Expr::Const(0), "Z".to_string() => Expr::Const(0) }
1395        );
1396
1397        let common_context = CommonContext::new();
1398        let parse_result = parse_str(
1399            "\
1400        .if 4 > 5
1401        .define X
1402        .elif 2 > 1
1403        .define Y
1404        .else
1405        .define T
1406        .endif
1407        .define Z",
1408            &common_context,
1409        );
1410        assert!(parse_result.is_ok());
1411
1412        assert_eq!(
1413            common_context.defines.borrow().clone(),
1414            hashmap! { "Y".to_string() => Expr::Const(0), "Z".to_string() => Expr::Const(0) },
1415        );
1416    }
1417
1418    #[test]
1419    fn check_directive_exit() {
1420        let common_context = CommonContext::new();
1421        let parse_result = parse_str(".define Y\n.exit\n.define X", &common_context);
1422        assert!(parse_result.is_ok());
1423
1424        assert_eq!(
1425            common_context.defines.borrow().clone(),
1426            hashmap! { "Y".to_string() => Expr::Const(0) },
1427        );
1428    }
1429
1430    #[test]
1431    fn check_directive_macro() {
1432        let common_context = CommonContext::new();
1433        let parse_result = parse_str(
1434            "
1435.macro test
1436    ; bla bla bla
1437    mov r0, @1
1438    mov r2, @0
1439.if @2 > 0x40
1440    lds r16, @2
1441.endif
1442.endm
1443.macro test2
1444.endmacro
1445
1446        ",
1447            &common_context,
1448        );
1449        assert_eq!(
1450            parse_result.unwrap(),
1451            ParseResult {
1452                segments: vec![],
1453                macroses: hashmap! {
1454                    "test".to_string() => vec! [
1455                        (CodePoint{ line_num: 2, num: 3}, "    ; bla bla bla".to_string()),
1456                        (CodePoint{ line_num: 3, num: 3}, "    mov r0, @1".to_string()),
1457                        (CodePoint{ line_num: 4, num: 3}, "    mov r2, @0".to_string()),
1458                        (CodePoint{ line_num: 5, num: 3}, ".if @2 > 0x40".to_string()),
1459                        (CodePoint{ line_num: 6, num: 3}, "    lds r16, @2".to_string()),
1460                        (CodePoint{ line_num: 7, num: 3}, ".endif".to_string()),
1461                    ],
1462                    "test2".to_string() => vec![]
1463                },
1464                messages: vec![],
1465            }
1466        );
1467    }
1468
1469    #[test]
1470    fn check_directive_pragma() {
1471        let common_context = CommonContext::new();
1472        let parse_result = parse_str(
1473            "
1474.pragma option use core v1
1475        ",
1476            &common_context,
1477        );
1478        assert_eq!(
1479            parse_result.unwrap(),
1480            ParseResult {
1481                segments: vec![Segment {
1482                    items: vec![(
1483                        CodePoint {
1484                            line_num: 2,
1485                            num: 2
1486                        },
1487                        Item::Pragma(vec![
1488                            Operand::E(Expr::Ident("option".to_string())),
1489                            Operand::E(Expr::Ident("use".to_string())),
1490                            Operand::E(Expr::Ident("core".to_string())),
1491                            Operand::E(Expr::Ident("v1".to_string())),
1492                        ])
1493                    )],
1494                    t: SegmentType::Code,
1495                    address: 0
1496                }],
1497                macroses: hashmap! {},
1498                messages: vec![],
1499            }
1500        );
1501    }
1502
1503    #[test]
1504    fn check_directive_cseg_size() {
1505        let common_context = CommonContext::new();
1506        let parse_result = parse_str(
1507            "
1508.csegsize 11
1509        ",
1510            &common_context,
1511        );
1512        assert!(parse_result.is_ok());
1513    }
1514
1515    #[test]
1516    fn check_directive_message_warning_error() {
1517        let common_context = CommonContext::new();
1518        let parse_result = parse_str(
1519            "
1520.message \"test\"
1521.warning \"test 2\"
1522        ",
1523            &common_context,
1524        );
1525        assert_eq!(
1526            parse_result.unwrap(),
1527            ParseResult {
1528                segments: vec![],
1529                macroses: hashmap! {},
1530                messages: vec![
1531                    "info: test in line: 2".to_string(),
1532                    "warning: test 2 in line: 3".to_string(),
1533                ],
1534            }
1535        );
1536
1537        let common_context = CommonContext::new();
1538        let parse_result = parse_str(
1539            "
1540.error \"Fail Fail Fail\"
1541        ",
1542            &common_context,
1543        );
1544        assert_eq!(parse_result.is_err(), true,);
1545    }
1546}