1use 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 rule space() = [' ' | '\t']*
30
31 rule ne_space() = [' ' | '\t']+
33
34 rule new_line() = ['\n' | '\r']*
36
37 rule char_ident() = ['0'..='9' | 'a'..='z' | 'A'..='Z' | '_' ]
38
39 pub rule ident() -> Document
41 = i:$(['a'..='z' | 'A'..='Z' | '_' ] char_ident()*) {
42 Document::Ident(i.parse().unwrap())
43 }
44
45 pub rule label() -> Document
47 = l:$(ident())":" {
48 Document::Label(l.to_lowercase())
49 }
50
51 pub rule string() -> String
54 = "\"" s:$((!("\"" / "\n" / "\r") [_])*) "\"" { s.to_string() }
55
56 pub rule ch() -> char
59 = "'" c:$((!("'" / "\n" / "\r") [_])*<1>) "'" { c.chars().next().unwrap() }
60
61
62 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 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 pub rule expr() -> Expr
78 = precedence! {
79 x:(@) space() "||" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LogicalOr, right: y})) }
81 --
82 x:(@) space() "&&" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::LogicalAnd, right: y})) }
84 --
85 x:(@) space() "|" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseOr, right: y})) }
87 --
88 x:(@) space() "^" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseXor, right: y})) }
90 --
91 x:(@) space() "&" space() y:@ { Expr::Binary(Box::new(BinaryExpr{left: x, operator: BinaryOperator::BitwiseAnd, right: y})) }
93 --
94 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 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 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 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 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 "-" v:@ { Expr::Unary(Box::new(UnaryExpr{operator: UnaryOperator::Minus, expr: v})) }
121 --
122 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 pub rule op_list() -> Vec<InstructionOps>
202 = instruction_ops() ** delimiter()
203
204 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 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 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 pub rule directive_ops() -> DirectiveOps
230 = a:e_ident() space() "=" space() e:expr() { DirectiveOps::Assign(a, e) }
231 / 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 / ol: directive_op() ** delimiter() { DirectiveOps::OpList(ol) }
239
240 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 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}