avra_lib/
document.rs

1//! Contains rules for document parsing of AVRA-rs
2
3use std::str::FromStr;
4
5use peg::parser;
6
7use crate::{
8    directive::{Directive, DirectiveOps, Operand},
9    expr::{BinaryExpr, BinaryOperator, Expr, UnaryExpr, UnaryOperator},
10    instruction::{
11        operation::{BranchT, Operation, SFlags},
12        register::{Reg16, Reg8},
13        IndexOps, InstructionOps,
14    },
15};
16
17#[derive(Clone, PartialEq, Eq, Debug)]
18pub enum Document {
19    EmptyLine,
20    Ident(String),
21    Label(String),
22    CodeLine(Box<Option<Document>>, Operation, Vec<InstructionOps>),
23    DirectiveLine(Box<Option<Document>>, Directive, DirectiveOps),
24}
25
26parser! {
27    pub grammar document() for str {
28        // spaces
29        rule space() = [' ' | '\t']*
30
31        // no empty spaces
32        rule ne_space() = [' ' | '\t']+
33
34        // new line
35        rule new_line() = ['\n' | '\r']*
36
37        rule char_ident() = ['0'..='9' | 'a'..='z' | 'A'..='Z' | '_' ]
38
39        // ident
40        pub rule ident() -> Document
41            = i:$(['a'..='z' | 'A'..='Z' | '_' ] char_ident()*) {
42            Document::Ident(i.parse().unwrap())
43        }
44
45        // label
46        pub rule label() -> Document
47            = l:$(ident())":" {
48            Document::Label(l.to_lowercase())
49        }
50
51        // string
52        // note: escaped chars not handled
53        pub rule string() -> String
54            = "\"" s:$((!("\"" / "\n" / "\r") [_])*) "\"" { s.to_string() }
55
56        // char
57        // note: escaped chars not handled
58        pub rule ch() -> char
59            = "'" c:$((!("'" / "\n" / "\r") [_])*<1>) "'" { c.chars().next().unwrap() }
60
61
62        // expression ident
63        pub rule e_ident() -> Expr
64            = i:$(['a'..='z' | 'A'..='Z' | '_' ]['0'..='9' | 'a'..='z' | 'A'..='Z' | '_' ]*) {
65            Expr::Ident(i.parse().unwrap())
66        }
67
68        // expression const
69        pub rule e_const() -> Expr
70            = "$" n:$(['0'..='9' | 'A'..='F' | 'a'..='f']+) { Expr::Const(i64::from_str_radix(n, 16).unwrap()) }
71            / "0x" n:$(['0'..='9' | 'A'..='F' | 'a'..='f']+) { Expr::Const(i64::from_str_radix(n, 16).unwrap()) }
72            / "0b" n:$(['0'..='1']+) { Expr::Const(i64::from_str_radix(n, 2).unwrap()) }
73            / "0" n:$(['0'..='7']+) { Expr::Const(i64::from_str_radix(n, 8).unwrap()) }
74            / n:$(['0'..='9']+) { Expr::Const(n.parse().unwrap()) }
75
76        // expression
77        pub rule expr() -> Expr
78            = precedence! {
79            // precedence 4
80            x:(@) space() "||" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LogicalOr, right: y})) }
81            --
82            // precedence 5
83            x:(@) space() "&&" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LogicalAnd, right: y})) }
84            --
85            // precedence 6
86            x:(@) space() "|" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseOr, right: y})) }
87            --
88            // precedence 7
89            x:(@) space() "^" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseXor, right: y})) }
90            --
91            // precedence 8
92            x:(@) space() "&" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseAnd, right: y})) }
93            --
94            // precedence 9
95            x:(@) space() "==" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Equal, right: y})) }
96            x:(@) space() "!=" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::NotEqual, right: y})) }
97            --
98            // precedence 10
99            x:(@) space() "<" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LessThan, right: y})) }
100            x:(@) space() "<=" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LessOrEqual, right: y})) }
101            x:(@) space() ">" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::GreaterThan, right: y})) }
102            x:(@) space() ">=" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::GreaterOrEqual, right: y})) }
103            --
104            // precedence 11
105            x:(@) space() "<<" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::ShiftLeft, right: y})) }
106            x:(@) space() ">>" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::ShiftRight, right: y})) }
107            --
108            // precedence 12
109            x:(@) space() "+" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Add, right: y})) }
110            x:(@) space() "-" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Sub, right: y})) }
111            "~" v:@ { Expr::Unary(Box::new(UnaryExpr{operator: UnaryOperator::BitwiseNot, expr: v})) }
112            "!" v:@ { Expr::Unary(Box::new(UnaryExpr{operator: UnaryOperator::LogicalNot, expr: v})) }
113            --
114            // precedence 13
115            x:(@) space() "*" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Mul, right: y})) }
116            x:(@) space() "/" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Div, right: y})) }
117            x:(@) space() "%" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::Rem, right: y})) }
118            --
119            // precedence 14
120            "-" v:@ { Expr::Unary(Box::new(UnaryExpr{operator: UnaryOperator::Minus, expr: v})) }
121            --
122            // precedence 15
123            n:e_ident() space() "(" space() args:expr() space() ")" { Expr::Func(Box::new(n), Box::new(args)) }
124            "(" space() be:expr() space() ")" { be }
125            c:e_const() { c }
126            c:ch() { Expr::Const(c as i64) }
127            i:e_ident() { i }
128        }
129
130        rule branc_op() -> &'input str
131            = $("eq" / "ne" / "cs" / "cc"
132            / "sh" / "lo" / "mi" / "pl"
133            / "ge" / "lt" / "hs" / "hc"
134            / "ts" / "tc" / "vs" / "vc"
135            / "ie" / "id" / "bs" / "bc")
136
137        rule flag_op() -> &'input str
138            = $("c" / "z" / "n" / "v" / "s" / "h" / "t" / "i")
139
140        rule op() -> &'input str
141            = $("wdr" / "tst" / "swap" / "subi" / "sub" / "sts" / "std" / "st" / "spm"
142            / "sleep" / "ser" / "sbrs" / "sbrc" / "sbr" / "sbiw" / "sbis" / "sbic" / "sbi"
143            / "sbci" / "sbc" / "ror" / "rol" / "rjmp" / "reti" / "ret" / "rcall" / "push"
144            / "pop" / "out" / "ori" / "or" / "nop" / "neg" / "mulsu" / "muls" / "mul" / "movw"
145            / "mov" / "lsr" / "lsl" / "lpm" / "lds" / "ldi" / "ldd" / "ld" / "jmp" / "inc"
146            / "in" / "ijmp" / "icall" / "fmulsu" / "fmuls" / "fmul" / "eor" / "elpm" / "eijmp"
147            / "eicall" / "dec" / "cpse" / "cpi" / "cpc" / "cp" / "com" / "clr" / "cbr" / "cbi"
148            / "call" / "bst" / "bset" / "break" / "bld" / "bclr" / "asr" / "andi" / "and" / "adiw"
149            / "add" / "adc")
150
151        pub rule standard_operation() -> Operation
152            = "br" br_type:branc_op() { Operation::Br(BranchT::from_str(br_type).unwrap()) }
153            / "se" se_type:flag_op() { Operation::Se(SFlags::from_str(se_type).unwrap()) }
154            / "cl" cl_type:flag_op() { Operation::Cl(SFlags::from_str(cl_type).unwrap()) }
155            / op_name:op() { Operation::from_str(op_name).unwrap() }
156
157        pub rule operation() -> Operation
158            = op_name:$(ident()) {
159                if let Ok(op) = document::standard_operation(op_name.to_lowercase().as_str()) {
160                    op
161                } else {
162                    Operation::Custom(op_name.to_lowercase())
163                }
164            }
165
166        pub rule reg8() -> Reg8
167            = r_name:$(['r' | 'R'] ['0'..='9']*<1,2>) { Reg8::from_str(r_name.to_lowercase().as_str()).unwrap() }
168
169        pub rule reg16() -> Reg16
170            = r_name:$(['x' | 'y' | 'z' | 'X' | 'Y' | 'Z']) { Reg16::from_str(r_name.to_lowercase().as_str()).unwrap() }
171
172        pub rule index_ops() -> IndexOps
173            = "-" r:reg16() { IndexOps::PreDecrement(r) }
174            / r:reg16() "+" e:expr() { IndexOps::PostIncrementE(r, e) }
175            / r:reg16() "+" { IndexOps::PostIncrement(r) }
176            / r:reg16() !char_ident() { IndexOps::None(r) }
177
178
179        pub rule instruction_ops() -> InstructionOps
180            = ind:index_ops() { InstructionOps::Index(ind) }
181            / r8:reg8() { InstructionOps::R8(r8) }
182            / e:expr() { InstructionOps::E(e) }
183
184
185        pub rule standard_directive() -> Result<Directive, strum::ParseError>
186            = d:$(ident()) { Directive::from_str(d) }
187
188        pub rule directive() -> Directive
189            = ("." / "#" ) d_name:$(['a'..='z']+) {
190            if let Ok(Ok(d)) = document::standard_directive(d_name.to_lowercase().as_str()) {
191                d
192            } else {
193                Directive::Custom(d_name.to_string())
194            }
195        }
196
197        rule delimiter()
198            = space() "," space()
199
200        // operands list
201        pub rule op_list() -> Vec<InstructionOps>
202            = instruction_ops() ** delimiter()
203
204        // comment
205        rule asm_comment() = ";" [_]* new_line()
206
207        rule c_comment() = "/*" (!("*/" / "\n" / "\r") [_])* "*/" new_line()
208
209        rule c_another_comment() = "//" [_]* new_line()
210
211        pub rule comment()
212            = asm_comment()
213            / c_comment()
214            / c_another_comment()
215
216        // instruction line
217        pub rule instruction_line() -> Document
218            = l:label()? space() o:operation() space() ol:op_list() space() comment()? {Document::CodeLine(Box::new(l), o, ol)}
219
220        // directive operand
221        pub rule directive_op() ->  Operand
222            = e:expr() { Operand::E(e) }
223            / s:string() { Operand::S(s) }
224
225        rule delimiter_space()
226            = ne_space() space()
227
228        // directive operands
229        pub rule directive_ops() -> DirectiveOps
230            = a:e_ident() space() "=" space() e:expr() { DirectiveOps::Assign(a, e) }
231            // Start pragma hack
232            / a:directive_op() ne_space() b:directive_op() ne_space() c:directive_op() ne_space() d:directive_op() ne_space() e:directive_op() ne_space() f:directive_op() { DirectiveOps::OpList(vec![a, b, c, d, e, f]) }
233            / a:directive_op() ne_space() b:directive_op() ne_space() c:directive_op() ne_space() d:directive_op() ne_space() e:directive_op() { DirectiveOps::OpList(vec![a, b, c, d, e]) }
234            / a:directive_op() ne_space() b:directive_op() ne_space() c:directive_op() ne_space() d:directive_op() { DirectiveOps::OpList(vec![a, b, c, d]) }
235            / a:directive_op() ne_space() b:directive_op() ne_space() c:directive_op() { DirectiveOps::OpList(vec![a, b, c]) }
236            / a:directive_op() ne_space() b:directive_op() { DirectiveOps::OpList(vec![a, b]) }
237            // End pragma hack
238            / ol: directive_op() ** delimiter() { DirectiveOps::OpList(ol) }
239
240        // directive line
241        pub rule directive_line() -> Document
242            = l:label()? space() d:directive() space() os:directive_ops() space() comment()? { Document::DirectiveLine(Box::new(l), d, os) }
243
244        // line
245        pub rule line() -> Document
246            = d_l:directive_line() { d_l }
247            / i_l:instruction_line() { i_l }
248            / l:label() space() comment()? { l }
249            / space() comment() { Document::EmptyLine }
250            / space() new_line() { Document::EmptyLine }
251    }
252}
253
254#[cfg(test)]
255mod parser_tests {
256    use super::*;
257
258    #[test]
259    fn ident_test() {
260        assert_eq!(document::ident("_"), Ok(Document::Ident("_".to_string())));
261
262        assert_eq!(document::ident("l"), Ok(Document::Ident("l".to_string())));
263
264        assert_eq!(
265            document::ident("Test_string_of_ident"),
266            Ok(Document::Ident("Test_string_of_ident".to_string()))
267        );
268
269        assert!(document::ident("4").is_err());
270    }
271
272    #[test]
273    fn label_test() {
274        assert_eq!(document::label("_:"), Ok(Document::Label("_".to_string())));
275
276        assert_eq!(
277            document::label("test_label:"),
278            Ok(Document::Label("test_label".to_string()))
279        );
280
281        assert!(document::ident("4").is_err());
282
283        assert!(document::ident("4:").is_err());
284    }
285
286    #[test]
287    fn string_test() {
288        assert_eq!(document::string("\"\""), Ok("".to_string()));
289
290        assert_eq!(document::string("\"X\""), Ok("X".to_string()));
291
292        assert_eq!(
293            document::string("\"Bla bla bla x,jljlsdfsfsdf//()_\t\""),
294            Ok("Bla bla bla x,jljlsdfsfsdf//()_\t".to_string())
295        );
296    }
297
298    #[test]
299    fn ch_test() {
300        assert_eq!(document::ch("'x'"), Ok('x'));
301    }
302
303    #[test]
304    fn comment_test() {
305        assert_eq!(document::comment(";"), Ok(()));
306
307        assert_eq!(document::comment("; "), Ok(()));
308
309        assert_eq!(document::comment("; bla bla bla"), Ok(()));
310
311        assert_eq!(document::comment("; bla bla bla\n"), Ok(()));
312    }
313
314    #[test]
315    fn line_test() {
316        assert_eq!(document::line(""), Ok(Document::EmptyLine));
317
318        assert_eq!(document::line("\n"), Ok(Document::EmptyLine));
319
320        assert_eq!(
321            document::line("\t\t; this is more comment\n"),
322            Ok(Document::EmptyLine)
323        );
324
325        assert_eq!(
326            document::line("test_label:"),
327            Ok(Document::Label("test_label".to_string()))
328        );
329
330        assert_eq!(
331            document::line("test_label: ; with comment"),
332            Ok(Document::Label("test_label".to_string()))
333        );
334
335        assert_eq!(
336            document::line("cli"),
337            Ok(Document::CodeLine(
338                Box::new(None),
339                Operation::Cl(SFlags::I),
340                vec![]
341            ))
342        );
343
344        assert_eq!(
345            document::line("push r0"),
346            Ok(Document::CodeLine(
347                Box::new(None),
348                Operation::Push,
349                vec![InstructionOps::R8(Reg8::R0)]
350            ))
351        );
352
353        assert_eq!(
354            document::line("ldi r16, 1 << 4 | 1 << 2"),
355            Ok(Document::CodeLine(
356                Box::new(None),
357                Operation::Ldi,
358                vec![
359                    InstructionOps::R8(Reg8::R16),
360                    InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
361                        left: Expr::Binary(Box::new(BinaryExpr {
362                            left: Expr::Const(1),
363                            operator: BinaryOperator::ShiftLeft,
364                            right: Expr::Const(4)
365                        })),
366                        operator: BinaryOperator::BitwiseOr,
367                        right: Expr::Binary(Box::new(BinaryExpr {
368                            left: Expr::Const(1),
369                            operator: BinaryOperator::ShiftLeft,
370                            right: Expr::Const(2)
371                        }))
372                    })))
373                ]
374            ))
375        );
376
377        assert_eq!(
378            document::line("ldi r18, high(t)"),
379            Ok(Document::CodeLine(
380                Box::new(None),
381                Operation::Ldi,
382                vec![
383                    InstructionOps::R8(Reg8::R18),
384                    InstructionOps::E(Expr::Func(
385                        Box::new(Expr::Ident("high".to_string())),
386                        Box::new(Expr::Ident("t".to_string()))
387                    ))
388                ]
389            ))
390        );
391
392        assert_eq!(
393            document::line("ld r19, X+"),
394            Ok(Document::CodeLine(
395                Box::new(None),
396                Operation::Ld,
397                vec![
398                    InstructionOps::R8(Reg8::R19),
399                    InstructionOps::Index(IndexOps::PostIncrement(Reg16::X))
400                ]
401            ))
402        );
403
404        assert_eq!(
405            document::line("outi UDR0, r16, 1 << 4 | 1 << 2"),
406            Ok(Document::CodeLine(
407                Box::new(None),
408                Operation::Custom("outi".to_string()),
409                vec![
410                    InstructionOps::E(Expr::Ident("UDR0".to_string())),
411                    InstructionOps::R8(Reg8::R16),
412                    InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
413                        left: Expr::Binary(Box::new(BinaryExpr {
414                            left: Expr::Const(1),
415                            operator: BinaryOperator::ShiftLeft,
416                            right: Expr::Const(4)
417                        })),
418                        operator: BinaryOperator::BitwiseOr,
419                        right: Expr::Binary(Box::new(BinaryExpr {
420                            left: Expr::Const(1),
421                            operator: BinaryOperator::ShiftLeft,
422                            right: Expr::Const(2)
423                        }))
424                    })))
425                ]
426            ))
427        );
428
429        assert_eq!(
430            document::line("fail: reti; exit from interrupt"),
431            Ok(Document::CodeLine(
432                Box::new(Some(Document::Label("fail".to_string()))),
433                Operation::Reti,
434                vec![]
435            ))
436        );
437
438        assert_eq!(
439            document::line(".dseg"),
440            Ok(Document::DirectiveLine(
441                Box::new(None),
442                Directive::DSeg,
443                DirectiveOps::OpList(vec![])
444            ))
445        );
446
447        assert_eq!(
448            document::line(".equ Last = 8"),
449            Ok(Document::DirectiveLine(
450                Box::new(None),
451                Directive::Equ,
452                DirectiveOps::Assign(Expr::Ident("Last".to_string()), Expr::Const(8))
453            ))
454        );
455    }
456}