avra_lib/
parser.rs

1//! Contains content parser of AVRA-rs
2
3use std::{
4    cell::RefCell,
5    collections::{BTreeSet, HashMap},
6    env, fmt,
7    fs::File,
8    io::Read,
9    iter::Iterator,
10    path::PathBuf,
11    rc::Rc,
12};
13
14use crate::{
15    context::CommonContext,
16    directive::{Directive, Operand},
17    document::{document, Document},
18    expr::Expr,
19    instruction::{operation::Operation, InstructionOps},
20};
21
22use failure::{bail, Error};
23use maplit::{btreeset, hashmap};
24use strum_macros::Display;
25
26pub type Paths = BTreeSet<PathBuf>;
27
28// TODO: add file name display support
29#[derive(Clone, Copy, PartialEq, Eq, Debug)]
30pub struct CodePoint {
31    pub line_num: usize,
32    pub num: usize,
33}
34
35impl fmt::Display for CodePoint {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        write!(f, "line: {}", self.line_num)
38    }
39}
40
41#[derive(Clone, PartialEq, Eq, Debug)]
42pub enum DataDefine {
43    Db,
44    Dw,
45    Dd,
46    Dq,
47}
48
49#[derive(Clone, PartialEq, Eq, Debug)]
50pub enum Item {
51    ReserveData(i64),
52    Data(DataDefine, Vec<Operand>),
53    Def(String, Expr),
54    Undef(String),
55    Set(String, Expr),
56    Pragma(Vec<Operand>),
57    Instruction(Operation, Vec<InstructionOps>),
58    Label(String),
59}
60
61#[derive(Clone, Copy, PartialEq, Eq, Debug, Display)]
62#[strum(serialize_all = "lowercase")]
63pub enum SegmentType {
64    Code,
65    Data,
66    Eeprom,
67}
68
69#[derive(Clone, PartialEq, Eq, Debug)]
70pub struct Segment {
71    pub items: Vec<(CodePoint, Item)>,
72    pub t: SegmentType,
73    /// start segment address
74    pub address: u32,
75}
76
77impl Segment {
78    pub fn new(t: SegmentType) -> Self {
79        Self {
80            items: vec![],
81            t,
82            address: 0,
83        }
84    }
85
86    pub fn is_empty(&self) -> bool {
87        self.items.is_empty()
88    }
89}
90
91#[derive(Clone, PartialEq, Eq, Debug)]
92pub struct ParseResult {
93    // collect of segments
94    pub segments: Vec<Segment>,
95    // macroses
96    pub macroses: HashMap<String, Vec<(CodePoint, String)>>,
97    // messages
98    pub messages: Vec<String>,
99}
100
101impl ParseResult {
102    pub fn new() -> Self {
103        Self {
104            segments: vec![],
105            macroses: hashmap! {},
106            messages: vec![],
107        }
108    }
109}
110
111#[derive(Clone, PartialEq, Eq, Debug)]
112pub struct Macro {
113    pub name: RefCell<String>,
114    pub macroses: RefCell<HashMap<String, Vec<(CodePoint, String)>>>,
115}
116
117impl Macro {
118    pub fn new() -> Self {
119        Self {
120            name: RefCell::new(String::new()),
121            macroses: RefCell::new(hashmap! {}),
122        }
123    }
124}
125
126#[derive(Clone, PartialEq, Eq, Debug)]
127pub struct ParseContext {
128    pub current_path: PathBuf,
129    pub include_paths: RefCell<Paths>,
130    // common part
131    pub common_context: CommonContext,
132    // segments
133    pub segments: Rc<RefCell<Vec<Rc<RefCell<Segment>>>>>,
134    // macro
135    pub macros: Rc<Macro>,
136    // messages
137    pub messages: Rc<RefCell<Vec<String>>>,
138}
139
140impl ParseContext {
141    pub fn new(
142        current_path: PathBuf,
143        include_paths: RefCell<Paths>,
144        common_context: CommonContext,
145    ) -> Self {
146        Self {
147            current_path,
148            include_paths,
149            common_context,
150            segments: Rc::new(RefCell::new(vec![Rc::new(RefCell::new(Segment::new(
151                SegmentType::Code,
152            )))])),
153            macros: Rc::new(Macro {
154                name: RefCell::new(String::new()),
155                macroses: RefCell::new(hashmap! {}),
156            }),
157            messages: Rc::new(RefCell::new(vec![])),
158        }
159    }
160
161    pub fn add_segment(&self, segment: Segment) {
162        self.segments
163            .borrow_mut()
164            .push(Rc::new(RefCell::new(segment)));
165    }
166
167    pub fn last_segment(&self) -> Option<Rc<RefCell<Segment>>> {
168        if let Some(item) = self.segments.borrow().last() {
169            Some(Rc::clone(item))
170        } else {
171            None
172        }
173    }
174
175    pub fn push_to_last(&self, item: (CodePoint, Item)) {
176        self.last_segment().unwrap().borrow_mut().items.push(item);
177    }
178
179    pub fn as_parse_result(&self) -> ParseResult {
180        let segments = self
181            .segments
182            .borrow()
183            .iter()
184            .filter(|x| !x.borrow().is_empty())
185            .map(|x| x.borrow().clone())
186            .collect();
187        let macroses = self.macros.macroses.borrow().clone();
188        let messages = self.messages.borrow().clone();
189
190        ParseResult {
191            segments,
192            macroses,
193            messages,
194        }
195    }
196}
197
198pub fn parse_str(input: &str, common_context: &CommonContext) -> Result<ParseResult, Error> {
199    let context = ParseContext::new(
200        env::current_dir()?,
201        RefCell::new(btreeset! {}),
202        common_context.clone(),
203    );
204
205    parse(input, &context)?;
206
207    Ok(context.as_parse_result())
208}
209
210pub fn parse_file(
211    path: PathBuf,
212    paths: Paths,
213    common_context: &CommonContext,
214) -> Result<ParseResult, Error> {
215    let context = ParseContext::new(path, RefCell::new(paths), common_context.clone());
216
217    parse_file_internal(&context)?;
218
219    Ok(context.as_parse_result())
220}
221
222pub fn parse_file_internal(context: &ParseContext) -> Result<(), Error> {
223    let ParseContext {
224        current_path,
225        include_paths,
226        common_context,
227        segments,
228        macros,
229        messages,
230    } = context.clone();
231    let include_paths = include_paths.borrow_mut();
232
233    let current_path = if !current_path.as_path().exists() {
234        let mut new_path = PathBuf::new();
235        for parent in include_paths.iter() {
236            let mut full_path = parent.clone();
237            full_path.push(current_path.clone());
238            if full_path.as_path().exists() {
239                new_path = full_path;
240                break;
241            }
242        }
243
244        if new_path == PathBuf::new() {
245            current_path
246        } else {
247            new_path
248        }
249    } else {
250        current_path
251    };
252
253    let mut file = match File::open(&current_path) {
254        Ok(file) => file,
255        Err(err) => bail!(
256            "Cannot read file {} because: {}",
257            current_path.to_string_lossy(),
258            err
259        ),
260    };
261
262    let mut include_paths = include_paths.clone();
263    if let Some(parent) = current_path.parent() {
264        if let None = include_paths.get(parent) {
265            include_paths.insert(parent.to_path_buf());
266        }
267    }
268
269    let mut source = String::new();
270    file.read_to_string(&mut source)?;
271
272    let include_paths = RefCell::new(include_paths);
273
274    let context = ParseContext {
275        current_path,
276        include_paths,
277        common_context,
278        segments,
279        macros,
280        messages,
281    };
282
283    parse(source.as_str(), &context)?;
284
285    Ok(())
286}
287
288#[derive(Clone, Copy, PartialEq, Eq, Debug)]
289pub enum NextItem {
290    NewLine,
291    EndIf,
292    EndMacro,
293    EndFile,
294}
295
296fn skip<'a>(
297    iter: &mut dyn Iterator<Item = (usize, &'a str)>,
298    context: &ParseContext,
299    ni: NextItem,
300) -> Option<(usize, &'a str)> {
301    let mut scoup_count = 0;
302    match ni {
303        NextItem::NewLine => iter.next(),
304        NextItem::EndFile => None,
305        other => {
306            let mut ret = None;
307            if ni == NextItem::EndMacro {
308                let name = context.macros.name.borrow().clone();
309                let mut items = vec![];
310                while let Some((line_num, line)) = iter.next() {
311                    if let Ok(item) = document::line(line) {
312                        if let Document::DirectiveLine(_, directive, _) = item {
313                            if other == NextItem::EndMacro && directive == Directive::EndMacro
314                                || directive == Directive::EndM
315                            {
316                                ret = iter.next();
317                                break;
318                            }
319                        }
320                    }
321                    items.push((CodePoint { line_num, num: 3 }, line.to_string()));
322                }
323                context.macros.macroses.borrow_mut().insert(name, items);
324            } else {
325                while let Some((num, line)) = iter.next() {
326                    if let Ok(item) = document::line(line) {
327                        if let Document::DirectiveLine(_, directive, _) = item {
328                            if other == NextItem::EndIf {
329                                if directive == Directive::If
330                                    || directive == Directive::IfDef
331                                    || directive == Directive::IfNDef
332                                {
333                                    scoup_count += 1;
334                                } else if directive == Directive::Endif
335                                    || directive == Directive::Else
336                                    || directive == Directive::ElIf
337                                {
338                                    if scoup_count == 0 {
339                                        ret = if directive == Directive::ElIf {
340                                            Some((num, line))
341                                        } else {
342                                            iter.next()
343                                        };
344                                        break;
345                                    } else {
346                                        if directive == Directive::Endif {
347                                            scoup_count -= 1;
348                                        }
349                                    }
350                                }
351                            }
352                        }
353                    }
354                }
355            }
356            ret
357        }
358    }
359}
360
361pub fn parse(input: &str, context: &ParseContext) -> Result<(), Error> {
362    let mut lines = input.lines().enumerate();
363
364    parse_iter(&mut lines, context)
365}
366
367pub fn parse_iter<'a>(
368    iter: &mut dyn Iterator<Item = (usize, &'a str)>,
369    context: &ParseContext,
370) -> Result<(), Error> {
371    let mut next_item = NextItem::NewLine;
372
373    loop {
374        if let Some((line_num, line)) = skip(iter, context, next_item) {
375            next_item = NextItem::NewLine; // clear conditional flag to typical state
376            let line_num = line_num + 1;
377            let parsed_item = document::line(line);
378            if let Ok(item) = parsed_item {
379                match item {
380                    Document::Label(name) => {
381                        context.push_to_last((CodePoint { line_num, num: 1 }, Item::Label(name)));
382                    }
383                    Document::CodeLine(label, op, op_args) => {
384                        if let Some(label) = *label {
385                            if let Document::Label(name) = label {
386                                context.push_to_last((
387                                    CodePoint { line_num, num: 1 },
388                                    Item::Label(name),
389                                ));
390                            }
391                        }
392                        context.last_segment().unwrap().borrow_mut().items.push((
393                            CodePoint { line_num, num: 2 },
394                            Item::Instruction(op, op_args),
395                        ));
396                    }
397                    Document::DirectiveLine(label, d, d_op_args) => {
398                        if let Some(label) = *label {
399                            if let Document::Label(name) = label {
400                                context.push_to_last((
401                                    CodePoint { line_num, num: 1 },
402                                    Item::Label(name),
403                                ));
404                            }
405                        }
406                        let item = d.parse(&d_op_args, &context, CodePoint { line_num, num: 2 })?;
407                        next_item = item;
408                    }
409                    Document::EmptyLine => {}
410                    _ => {}
411                }
412            } else {
413                bail!(
414                    "failed to parse {} with error: {:?}",
415                    CodePoint { line_num, num: 1 },
416                    parsed_item
417                );
418            }
419        } else {
420            break;
421        }
422    }
423
424    Ok(())
425}
426
427#[cfg(test)]
428mod parser_tests {
429    use super::*;
430    use crate::{
431        expr::{BinaryExpr, BinaryOperator, UnaryExpr, UnaryOperator},
432        instruction::{
433            operation::{BranchT, SFlags},
434            register::{Reg16, Reg8},
435            IndexOps, InstructionOps,
436        },
437    };
438
439    use maplit::hashmap;
440
441    #[test]
442    fn check_empty() {
443        let parse_result = parse_str("", &CommonContext::new());
444
445        assert_eq!(parse_result.is_ok(), true);
446        assert_eq!(parse_result.unwrap(), ParseResult::new());
447
448        let parse_result = parse_str("\t\r\n\t     \t\r\n\n", &CommonContext::new());
449        assert_eq!(parse_result.is_ok(), true);
450        assert_eq!(parse_result.unwrap(), ParseResult::new());
451    }
452
453    #[test]
454    fn check_wrong() {
455        let parse_result = parse_str("bla bla bla bla", &CommonContext::new());
456        assert_eq!(parse_result.is_err(), true);
457    }
458
459    #[test]
460    fn check_comment() {
461        let parse_result = parse_str(";bla bla bla bla", &CommonContext::new());
462        assert_eq!(parse_result.unwrap(), ParseResult::new());
463
464        let parse_result = parse_str(";;bla bla bla bla", &CommonContext::new());
465        assert_eq!(parse_result.unwrap(), ParseResult::new());
466    }
467
468    #[test]
469    fn check_label() {
470        let parse_result = parse_str("good_point:", &CommonContext::new());
471        assert_eq!(
472            parse_result.unwrap(),
473            ParseResult {
474                segments: vec![Segment {
475                    items: vec![(
476                        CodePoint {
477                            line_num: 1,
478                            num: 1
479                        },
480                        Item::Label("good_point".to_string())
481                    )],
482                    t: SegmentType::Code,
483                    address: 0
484                }],
485                macroses: hashmap! {},
486                messages: vec![],
487            }
488        );
489
490        let parse_result = parse_str("good_point:\ngood_point2:", &CommonContext::new());
491        assert_eq!(
492            parse_result.unwrap(),
493            ParseResult {
494                segments: vec![Segment {
495                    items: vec![
496                        (
497                            CodePoint {
498                                line_num: 1,
499                                num: 1
500                            },
501                            Item::Label("good_point".to_string())
502                        ),
503                        (
504                            CodePoint {
505                                line_num: 2,
506                                num: 1
507                            },
508                            Item::Label("good_point2".to_string())
509                        )
510                    ],
511                    t: SegmentType::Code,
512                    address: 0
513                }],
514                macroses: hashmap! {},
515                messages: vec![],
516            }
517        );
518
519        let parse_result = parse_str("good_point:; this is simple comment", &CommonContext::new());
520        assert_eq!(
521            parse_result.unwrap(),
522            ParseResult {
523                segments: vec![Segment {
524                    items: vec![(
525                        CodePoint {
526                            line_num: 1,
527                            num: 1
528                        },
529                        Item::Label("good_point".to_string())
530                    )],
531                    t: SegmentType::Code,
532                    address: 0
533                }],
534                macroses: hashmap! {},
535                messages: vec![],
536            }
537        );
538
539        let parse_result = parse_str("bad_point: bla bla bla", &CommonContext::new());
540        assert_eq!(parse_result.is_err(), true);
541
542        let parse_result = parse_str("bad_point::", &CommonContext::new());
543        assert_eq!(parse_result.is_err(), true);
544
545        let parse_result = parse_str("bad_point: bla bla bla", &CommonContext::new());
546        assert_eq!(parse_result.is_err(), true);
547
548        let parse_result = parse_str("bad_point: bad_point2:", &CommonContext::new());
549        assert_eq!(parse_result.is_err(), true);
550    }
551
552    #[test]
553    fn check_non_argument_commands() {
554        let parse_result = parse_str("nop\nnop\nnop\nret", &CommonContext::new());
555        assert_eq!(
556            parse_result.unwrap(),
557            ParseResult {
558                segments: vec![Segment {
559                    items: vec![
560                        (
561                            CodePoint {
562                                line_num: 1,
563                                num: 2
564                            },
565                            Item::Instruction(Operation::Nop, vec![])
566                        ),
567                        (
568                            CodePoint {
569                                line_num: 2,
570                                num: 2
571                            },
572                            Item::Instruction(Operation::Nop, vec![])
573                        ),
574                        (
575                            CodePoint {
576                                line_num: 3,
577                                num: 2
578                            },
579                            Item::Instruction(Operation::Nop, vec![])
580                        ),
581                        (
582                            CodePoint {
583                                line_num: 4,
584                                num: 2
585                            },
586                            Item::Instruction(Operation::Ret, vec![])
587                        ),
588                    ],
589                    t: SegmentType::Code,
590                    address: 0
591                }],
592                macroses: hashmap! {},
593                messages: vec![],
594            }
595        );
596
597        let parse_result = parse_str("label:\nnop\nnop\nret", &CommonContext::new());
598        assert_eq!(
599            parse_result.unwrap(),
600            ParseResult {
601                segments: vec![Segment {
602                    items: vec![
603                        (
604                            CodePoint {
605                                line_num: 1,
606                                num: 1
607                            },
608                            Item::Label("label".to_string())
609                        ),
610                        (
611                            CodePoint {
612                                line_num: 2,
613                                num: 2
614                            },
615                            Item::Instruction(Operation::Nop, vec![])
616                        ),
617                        (
618                            CodePoint {
619                                line_num: 3,
620                                num: 2
621                            },
622                            Item::Instruction(Operation::Nop, vec![])
623                        ),
624                        (
625                            CodePoint {
626                                line_num: 4,
627                                num: 2
628                            },
629                            Item::Instruction(Operation::Ret, vec![])
630                        ),
631                    ],
632                    t: SegmentType::Code,
633                    address: 0
634                }],
635                macroses: hashmap! {},
636                messages: vec![],
637            }
638        );
639
640        let parse_result = parse_str("seh\nclh\nnop\ncli", &CommonContext::new());
641        assert_eq!(
642            parse_result.unwrap(),
643            ParseResult {
644                segments: vec![Segment {
645                    items: vec![
646                        (
647                            CodePoint {
648                                line_num: 1,
649                                num: 2
650                            },
651                            Item::Instruction(Operation::Se(SFlags::H), vec![])
652                        ),
653                        (
654                            CodePoint {
655                                line_num: 2,
656                                num: 2
657                            },
658                            Item::Instruction(Operation::Cl(SFlags::H), vec![])
659                        ),
660                        (
661                            CodePoint {
662                                line_num: 3,
663                                num: 2
664                            },
665                            Item::Instruction(Operation::Nop, vec![])
666                        ),
667                        (
668                            CodePoint {
669                                line_num: 4,
670                                num: 2
671                            },
672                            Item::Instruction(Operation::Cl(SFlags::I), vec![])
673                        ),
674                    ],
675                    t: SegmentType::Code,
676                    address: 0
677                }],
678                macroses: hashmap! {},
679                messages: vec![],
680            }
681        );
682    }
683
684    #[test]
685    fn check_one_argument_commands() {
686        let parse_result = parse_str("push r0\nlsl r0\nswap r0\npop r1", &CommonContext::new());
687        assert_eq!(
688            parse_result.unwrap(),
689            ParseResult {
690                segments: vec![Segment {
691                    items: vec![
692                        (
693                            CodePoint {
694                                line_num: 1,
695                                num: 2
696                            },
697                            Item::Instruction(Operation::Push, vec![InstructionOps::R8(Reg8::R0)])
698                        ),
699                        (
700                            CodePoint {
701                                line_num: 2,
702                                num: 2
703                            },
704                            Item::Instruction(Operation::Lsl, vec![InstructionOps::R8(Reg8::R0)])
705                        ),
706                        (
707                            CodePoint {
708                                line_num: 3,
709                                num: 2
710                            },
711                            Item::Instruction(Operation::Swap, vec![InstructionOps::R8(Reg8::R0)])
712                        ),
713                        (
714                            CodePoint {
715                                line_num: 4,
716                                num: 2
717                            },
718                            Item::Instruction(Operation::Pop, vec![InstructionOps::R8(Reg8::R1)])
719                        ),
720                    ],
721                    t: SegmentType::Code,
722                    address: 0
723                }],
724                macroses: hashmap! {},
725                messages: vec![],
726            }
727        );
728
729        let parse_result = parse_str("tst r1\nbrpl exit\nrjmp error\n", &CommonContext::new());
730        assert_eq!(
731            parse_result.unwrap(),
732            ParseResult {
733                segments: vec![Segment {
734                    items: vec![
735                        (
736                            CodePoint {
737                                line_num: 1,
738                                num: 2
739                            },
740                            Item::Instruction(Operation::Tst, vec![InstructionOps::R8(Reg8::R1)])
741                        ),
742                        (
743                            CodePoint {
744                                line_num: 2,
745                                num: 2
746                            },
747                            Item::Instruction(
748                                Operation::Br(BranchT::Pl),
749                                vec![InstructionOps::E(Expr::Ident("exit".to_string()))]
750                            )
751                        ),
752                        (
753                            CodePoint {
754                                line_num: 3,
755                                num: 2
756                            },
757                            Item::Instruction(
758                                Operation::Rjmp,
759                                vec![InstructionOps::E(Expr::Ident("error".to_string()))]
760                            )
761                        ),
762                    ],
763                    t: SegmentType::Code,
764                    address: 0
765                }],
766                macroses: hashmap! {},
767                messages: vec![],
768            }
769        );
770    }
771
772    #[test]
773    fn check_two_argument_commands() {
774        let parse_result = parse_str(
775            "ldi r16, 1 << 2 | 1 << 1\nmov r0, r16\n subi r16, (-1)\nsts data, r16",
776            &CommonContext::new(),
777        );
778        assert_eq!(
779            parse_result.unwrap(),
780            ParseResult {
781                segments: vec![Segment {
782                    items: vec![
783                        (
784                            CodePoint {
785                                line_num: 1,
786                                num: 2
787                            },
788                            Item::Instruction(
789                                Operation::Ldi,
790                                vec![
791                                    InstructionOps::R8(Reg8::R16),
792                                    InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
793                                        left: Expr::Binary(Box::new(BinaryExpr {
794                                            left: Expr::Const(1),
795                                            operator: BinaryOperator::ShiftLeft,
796                                            right: Expr::Const(2),
797                                        })),
798                                        operator: BinaryOperator::BitwiseOr,
799                                        right: Expr::Binary(Box::new(BinaryExpr {
800                                            left: Expr::Const(1),
801                                            operator: BinaryOperator::ShiftLeft,
802                                            right: Expr::Const(1),
803                                        })),
804                                    })))
805                                ]
806                            )
807                        ),
808                        (
809                            CodePoint {
810                                line_num: 2,
811                                num: 2
812                            },
813                            Item::Instruction(
814                                Operation::Mov,
815                                vec![InstructionOps::R8(Reg8::R0), InstructionOps::R8(Reg8::R16)]
816                            )
817                        ),
818                        (
819                            CodePoint {
820                                line_num: 3,
821                                num: 2
822                            },
823                            Item::Instruction(
824                                Operation::Subi,
825                                vec![
826                                    InstructionOps::R8(Reg8::R16),
827                                    InstructionOps::E(Expr::Unary(Box::new(UnaryExpr {
828                                        operator: UnaryOperator::Minus,
829                                        expr: Expr::Const(1)
830                                    })))
831                                ]
832                            )
833                        ),
834                        (
835                            CodePoint {
836                                line_num: 4,
837                                num: 2
838                            },
839                            Item::Instruction(
840                                Operation::Sts,
841                                vec![
842                                    InstructionOps::E(Expr::Ident("data".to_string())),
843                                    InstructionOps::R8(Reg8::R16)
844                                ]
845                            )
846                        ),
847                    ],
848                    t: SegmentType::Code,
849                    address: 0
850                }],
851                macroses: hashmap! {},
852                messages: vec![],
853            }
854        );
855
856        let parse_result = parse_str(
857            "ld r17, X\nld r18, Y+\nld r19, -Z\nst X+, r19",
858            &CommonContext::new(),
859        );
860        assert_eq!(
861            parse_result.unwrap(),
862            ParseResult {
863                segments: vec![Segment {
864                    items: vec![
865                        (
866                            CodePoint {
867                                line_num: 1,
868                                num: 2
869                            },
870                            Item::Instruction(
871                                Operation::Ld,
872                                vec![
873                                    InstructionOps::R8(Reg8::R17),
874                                    InstructionOps::Index(IndexOps::None(Reg16::X))
875                                ]
876                            )
877                        ),
878                        (
879                            CodePoint {
880                                line_num: 2,
881                                num: 2
882                            },
883                            Item::Instruction(
884                                Operation::Ld,
885                                vec![
886                                    InstructionOps::R8(Reg8::R18),
887                                    InstructionOps::Index(IndexOps::PostIncrement(Reg16::Y))
888                                ]
889                            )
890                        ),
891                        (
892                            CodePoint {
893                                line_num: 3,
894                                num: 2
895                            },
896                            Item::Instruction(
897                                Operation::Ld,
898                                vec![
899                                    InstructionOps::R8(Reg8::R19),
900                                    InstructionOps::Index(IndexOps::PreDecrement(Reg16::Z))
901                                ]
902                            )
903                        ),
904                        (
905                            CodePoint {
906                                line_num: 4,
907                                num: 2
908                            },
909                            Item::Instruction(
910                                Operation::St,
911                                vec![
912                                    InstructionOps::Index(IndexOps::PostIncrement(Reg16::X)),
913                                    InstructionOps::R8(Reg8::R19)
914                                ]
915                            )
916                        ),
917                    ],
918                    t: SegmentType::Code,
919                    address: 0
920                }],
921                macroses: hashmap! {},
922                messages: vec![],
923            }
924        );
925
926        let parse_result = parse_str("ldd r25, Z+2\nstd Z+6, r24", &CommonContext::new());
927        assert_eq!(
928            parse_result.unwrap(),
929            ParseResult {
930                segments: vec![Segment {
931                    items: vec![
932                        (
933                            CodePoint {
934                                line_num: 1,
935                                num: 2
936                            },
937                            Item::Instruction(
938                                Operation::Ldd,
939                                vec![
940                                    InstructionOps::R8(Reg8::R25),
941                                    InstructionOps::Index(IndexOps::PostIncrementE(
942                                        Reg16::Z,
943                                        Expr::Const(2)
944                                    ))
945                                ]
946                            )
947                        ),
948                        (
949                            CodePoint {
950                                line_num: 2,
951                                num: 2
952                            },
953                            Item::Instruction(
954                                Operation::Std,
955                                vec![
956                                    InstructionOps::Index(IndexOps::PostIncrementE(
957                                        Reg16::Z,
958                                        Expr::Const(6)
959                                    )),
960                                    InstructionOps::R8(Reg8::R24)
961                                ]
962                            )
963                        ),
964                    ],
965                    t: SegmentType::Code,
966                    address: 0
967                }],
968                macroses: hashmap! {},
969                messages: vec![],
970            }
971        );
972    }
973}