1use crate::types::*;
2use crate::util::*;
3use std::collections::HashMap;
4
5use nom::{
6 branch::alt,
7 bytes::complete::tag,
8 character::complete::{alpha1, alphanumeric1, char, none_of},
9 combinator::{map_res, opt, recognize},
10 error::{FromExternalError, ParseError},
11 multi::{many0, many0_count, many1, separated_list1},
12 number::complete::{double, float},
13 sequence::{delimited, pair, preceded, terminated, tuple},
14 IResult,
15};
16
17// STRING := '"' .... '"'
18fn string(input: &str) -> IResult<&str, String> {
19 // TODO: Does qbe support escaping in strings?
20 map_res(
21 delimited(char('"'), recognize(many0(none_of("\""))), char('"')),
22 |s| -> Result<String, ()> { Ok(String::from(s)) },
23 )(input)
24}
25
26// See https://c9x.me/git/qbe.git/tree/parse.c?h=v1.1#n302
27fn ident(input: &str) -> IResult<&str, String> {
28 map_res(
29 recognize(pair(
30 alt((alpha1, tag("."), tag("_"))),
31 many0_count(alt((alphanumeric1, tag("$"), tag("."), tag("_")))),
32 )),
33 |s| -> Result<String, ()> { Ok(String::from(s)) },
34 )(input)
35}
36
37// See https://c9x.me/compile/doc/il-v1.1.html#Sigils
38fn global(input: &str) -> IResult<&str, String> {
39 preceded(char('$'), ident)(input)
40}
41fn userdef(input: &str) -> IResult<&str, String> {
42 preceded(char(':'), ident)(input)
43}
44fn local(input: &str) -> IResult<&str, String> {
45 preceded(char('%'), ident)(input)
46}
47fn label(input: &str) -> IResult<&str, String> {
48 preceded(char('@'), ident)(input)
49}
50
51////////////////////////////////////////////////////////////////////////
52
53// BASETY := 'w' | 'l' | 's' | 'd'
54fn base_type(input: &str) -> IResult<&str, BaseType> {
55 alt((
56 bind(char('w'), BaseType::Word),
57 bind(char('l'), BaseType::Long),
58 bind(char('s'), BaseType::Single),
59 bind(char('d'), BaseType::Double),
60 ))(input)
61}
62
63// EXTTY := BASETY | 'b' | 'h'
64fn ext_type(input: &str) -> IResult<&str, ExtType> {
65 alt((
66 bind(char('b'), ExtType::Byte),
67 bind(char('h'), ExtType::Halfword),
68 map_res(base_type, |ty| -> Result<ExtType, ()> {
69 Ok(ExtType::Base(ty))
70 }),
71 ))(input)
72}
73
74// CONST :=
75// ['-'] NUMBER # Decimal integer
76// | 's_' FP # Single-precision float
77// | 'd_' FP # Double-precision float
78// | $IDENT # Global symbol
79fn constant(input: &str) -> IResult<&str, Const> {
80 // TODO: Ensure that floating point parser matches what QBE's parser does.
81 // See: https://c9x.me/git/qbe.git/tree/parse.c?h=v1.1#n245
82 alt((
83 map_res(
84 tuple((opt(char('-')), ws(parse_i64))),
85 |(pfx, n)| -> Result<Const, ()> {
86 match pfx {
87 Some(_) => Ok(Const::Number(-n)),
88 None => Ok(Const::Number(n)),
89 }
90 },
91 ),
92 preceded(
93 tag("s_"),
94 map_res(float, |f| -> Result<Const, ()> { Ok(Const::SFP(f)) }),
95 ),
96 preceded(
97 tag("d_"),
98 map_res(double, |f| -> Result<Const, ()> { Ok(Const::DFP(f)) }),
99 ),
100 map_res(global, |s| -> Result<Const, ()> { Ok(Const::Global(s)) }),
101 ))(input)
102}
103
104// DYNCONST :=
105// CONST
106// | 'thread' $IDENT # Thread-local symbol
107fn dynconstant(input: &str) -> IResult<&str, DynConst> {
108 alt((
109 map_res(constant, |c| -> Result<DynConst, ()> {
110 Ok(DynConst::Const(c))
111 }),
112 map_res(
113 preceded(tag("thread"), ws(global)),
114 |i| -> Result<DynConst, ()> { Ok(DynConst::Thread(i)) },
115 ),
116 ))(input)
117}
118
119// LINKAGE :=
120// 'export'
121// | 'thread'
122// | 'section' SECNAME
123// | 'section' SECNAME SECFLAGS
124//
125// SECNAME := '"' .... '"'
126// SECFLAGS := '"' .... '"'
127fn _linkage(input: &str) -> IResult<&str, Linkage> {
128 alt((
129 map_res(tag("export"), |_| -> Result<Linkage, ()> {
130 Ok(Linkage::Export)
131 }),
132 map_res(tag("thread"), |_| -> Result<Linkage, ()> {
133 Ok(Linkage::Thread)
134 }),
135 map_res(
136 tuple((tag("section"), ws(string), opt(string))),
137 |(_, secname, secflags)| -> Result<Linkage, ()> {
138 Ok(Linkage::Section(secname, secflags))
139 },
140 ),
141 ))(input)
142}
143
144// Parse linkage terminated with one or more newline characters.
145fn linkage(input: &str) -> IResult<&str, Linkage> {
146 terminated(ws(_linkage), newline0)(input)
147}
148
149// TYPEDEF := REGTY | OPAQUETY
150pub fn typedef(input: &str) -> IResult<&str, TypeDef> {
151 alt((regty, opaque))(input)
152}
153
154// REGTY :=
155// 'type' :IDENT '=' ['align' NUMBER]
156// '{'
157// ( SUBTY [NUMBER] ),
158// '}'
159fn regty(input: &str) -> IResult<&str, TypeDef> {
160 map_res(
161 tuple((
162 tag("type"),
163 ws(userdef),
164 char('='),
165 opt(preceded(ws(tag("align")), parse_u64)),
166 delimited(ws(char('{')), members, ws(char('}'))),
167 )),
168 |(_, id, _, align, members)| -> Result<TypeDef, ()> {
169 Ok(TypeDef {
170 name: id,
171 defn: AggregateType::Regular(align, members),
172 })
173 },
174 )(input)
175}
176
177// SUBTY := EXTTY | :IDENT
178fn subty(input: &str) -> IResult<&str, SubType> {
179 alt((
180 map_res(ext_type, |ty| -> Result<SubType, ()> {
181 Ok(SubType::ExtType(ty))
182 }),
183 map_res(userdef, |id| -> Result<SubType, ()> {
184 Ok(SubType::UserDef(id))
185 }),
186 ))(input)
187}
188
189// MEMBERS := ( MEMBER ),
190fn members(input: &str) -> IResult<&str, Vec<(SubType, u64)>> {
191 separated_list1(ws(char(',')), member)(input)
192}
193
194// MEMBER := SUBTY [NUMBER]
195fn member(input: &str) -> IResult<&str, (SubType, u64)> {
196 map_res(
197 pair(subty, opt(ws(parse_u64))),
198 |(ty, n)| -> Result<(SubType, u64), ()> { Ok((ty, n.unwrap_or(1))) },
199 )(input)
200}
201
202// OPAQUETY := 'type' :IDENT '=' 'align' NUMBER '{' NUMBER '}'
203fn opaque(input: &str) -> IResult<&str, TypeDef> {
204 map_res(
205 tuple((
206 tag("type"),
207 ws(userdef),
208 char('='),
209 ws(tag("align")),
210 parse_u64,
211 delimited(ws(char('{')), ws(parse_u64), char('}')),
212 )),
213 |(_, id, _, _, size, align)| -> Result<TypeDef, ()> {
214 Ok(TypeDef {
215 name: id,
216 defn: AggregateType::Opaque(size, align),
217 })
218 },
219 )(input)
220}
221
222// DATADEF :=
223// LINKAGE*
224// 'data' $IDENT '=' ['align' NUMBER]
225// '{'
226// ( EXTTY DATAITEM+
227// | 'z' NUMBER ),
228// '}'
229pub fn datadef(input: &str) -> IResult<&str, DataDef> {
230 map_res(
231 tuple((
232 many0(ws(linkage)),
233 ws(tag("data")),
234 global,
235 ws(char('=')),
236 opt(preceded(tag("align"), parse_u64)),
237 delimited(ws(char('{')), data_objs, char('}')),
238 )),
239 |(lnk, _, ident, _, align, objs)| -> Result<DataDef, ()> {
240 Ok(DataDef {
241 linkage: lnk,
242 name: ident,
243 align: align,
244 objs: objs,
245 })
246 },
247 )(input)
248}
249
250// DATAOBJS := ( DATAOBJ ),
251fn data_objs(input: &str) -> IResult<&str, Vec<DataObj>> {
252 separated_list1(ws(char(',')), data_obj)(input)
253}
254
255// DATAOBJ := EXTTY DATAITEM+
256// | 'z' NUMBER
257fn data_obj(input: &str) -> IResult<&str, DataObj> {
258 alt((
259 map_res(
260 pair(ext_type, many1(ws(data_item))),
261 |(ty, items)| -> Result<DataObj, ()> { Ok(DataObj::DataItem(ty, items)) },
262 ),
263 map_res(
264 preceded(char('z'), ws(parse_u64)),
265 |n| -> Result<DataObj, ()> { Ok(DataObj::ZeroFill(n)) },
266 ),
267 ))(input)
268}
269
270// DATAITEM :=
271// $IDENT ['+' NUMBER] # Symbol and offset
272// | '"' ... '"' # String
273// | CONST # Constant
274fn data_item(input: &str) -> IResult<&str, DataItem> {
275 alt((
276 map_res(
277 pair(global, opt(preceded(opt(ws(char('+'))), ws(parse_u64)))),
278 |(ident, off)| -> Result<DataItem, ()> { Ok(DataItem::Symbol(ident, off)) },
279 ),
280 map_res(string, |s| -> Result<DataItem, ()> {
281 Ok(DataItem::String(s))
282 }),
283 map_res(constant, |c| -> Result<DataItem, ()> {
284 Ok(DataItem::Const(c))
285 }),
286 ))(input)
287}
288
289// FUNCDEF :=
290// LINKAGE*
291// 'function' [ABITY] $IDENT PARAMS [NL]
292// '{' NL
293// BODY
294// '}'
295pub fn funcdef(input: &str) -> IResult<&str, FuncDef> {
296 map_res(
297 tuple((
298 many0(ws(linkage)),
299 ws(tag("function")),
300 opt(abity),
301 ws(global),
302 params,
303 ws(newline0),
304 delimited(ws(char('{')), preceded(newline1, body), char('}')),
305 )),
306 |(lnk, _, ret, id, params, _, body)| -> Result<FuncDef, ()> {
307 Ok(FuncDef {
308 linkage: lnk,
309 name: id,
310 abity: ret,
311 params: params,
312 body: body,
313 })
314 },
315 )(input)
316}
317
318// SUBWTY := 'sb' | 'ub' | 'sh' | 'uh'
319fn sub_word(input: &str) -> IResult<&str, SubWordType> {
320 alt((
321 str("sb", SubWordType::SignedByte),
322 str("ub", SubWordType::UnsignedHalf),
323 str("sh", SubWordType::SignedHalf),
324 str("uh", SubWordType::UnsignedHalf),
325 ))(input)
326}
327
328fn sub_long(input: &str) -> IResult<&str, SubLongType> {
329 alt((
330 str("uw", SubLongType::UnsignedWord),
331 str("sw", SubLongType::SignedWord),
332 map_res(sub_word, |ty| -> Result<SubLongType, ()> {
333 Ok(SubLongType::SubWord(ty))
334 }),
335 ))(input)
336}
337
338// See https://c9x.me/compile/doc/il-v1.1.html#Memory
339fn load_type(input: &str) -> IResult<&str, LoadType> {
340 alt((
341 map_res(sub_long, |ty| -> Result<LoadType, ()> {
342 Ok(LoadType::SubLong(ty))
343 }),
344 map_res(base_type, |ty| -> Result<LoadType, ()> {
345 Ok(LoadType::Base(ty))
346 }),
347 ))(input)
348}
349
350// ABITY := BASETY | SUBWTY | :IDENT
351fn abity(input: &str) -> IResult<&str, Type> {
352 alt((
353 map_res(base_type, |ty| -> Result<Type, ()> { Ok(Type::Base(ty)) }),
354 map_res(sub_word, |ty| -> Result<Type, ()> {
355 Ok(Type::SubWordType(ty))
356 }),
357 map_res(userdef, |str| -> Result<Type, ()> {
358 Ok(Type::UserDef(str))
359 }),
360 ))(input)
361}
362
363// PARAM :=
364// ABITY %IDENT # Regular parameter
365// | 'env' %IDENT # Environment parameter (first)
366// | '...' # Variadic marker (last)
367fn param(input: &str) -> IResult<&str, FuncParam> {
368 // TODO: Ensure that env is the first parameter
369 // TODO: Ensure that variadic marker is the last parameter
370 alt((
371 map_res(
372 pair(abity, ws(local)),
373 |(ty, id)| -> Result<FuncParam, ()> { Ok(FuncParam::Regular(ty, id)) },
374 ),
375 map_res(
376 preceded(tag("env"), ws(local)),
377 |id| -> Result<FuncParam, ()> { Ok(FuncParam::Env(id)) },
378 ),
379 map_res(tag("..."), |_| -> Result<FuncParam, ()> {
380 Ok(FuncParam::Variadic)
381 }),
382 ))(input)
383}
384
385// PARAMS = '(' (PARAM), ')'
386fn _params(input: &str) -> IResult<&str, Vec<FuncParam>> {
387 separated_list1(ws(char(',')), param)(input)
388}
389fn params(input: &str) -> IResult<&str, Vec<FuncParam>> {
390 delimited(char('('), ws(_params), char(')'))(input)
391}
392
393// BODY = BLOCK+
394fn body(input: &str) -> IResult<&str, Vec<Block>> {
395 many1(block)(input)
396}
397
398// BLOCK :=
399// @IDENT NL # Block label
400// ( PHI NL )* # Phi instructions
401// ( INST NL )* # Regular instructions
402// JUMP NL # Jump or return
403fn block(input: &str) -> IResult<&str, Block> {
404 map_res(
405 tuple((
406 terminated(label, ws(newline1)),
407 many0(terminated(phi, ws(newline1))),
408 many0(terminated(stat, ws(newline1))),
409 opt(terminated(jump, ws(newline1))),
410 )),
411 |(label, phis, inst, jump)| -> Result<Block, ()> {
412 Ok(Block {
413 label: label,
414 phi: phis,
415 inst: inst,
416 jump: jump,
417 })
418 },
419 )(input)
420}
421
422// JUMP :=
423// 'jmp' @IDENT # Unconditional
424// | 'jnz' VAL, @IDENT, @IDENT # Conditional
425// | 'ret' [VAL] # Return
426// | 'hlt' # Termination
427fn jump(input: &str) -> IResult<&str, JumpInstr> {
428 alt((
429 map_res(
430 preceded(tag("jmp"), ws(label)),
431 |l| -> Result<JumpInstr, ()> { Ok(JumpInstr::Jump(l)) },
432 ),
433 map_res(
434 tuple((
435 tag("jnz"),
436 ws(value),
437 char(','),
438 ws(label),
439 char(','),
440 ws(label),
441 )),
442 |(_, v, _, l1, _, l2)| -> Result<JumpInstr, ()> { Ok(JumpInstr::Jnz(v, l1, l2)) },
443 ),
444 map_res(
445 preceded(tag("ret"), opt(ws(value))),
446 |v| -> Result<JumpInstr, ()> { Ok(JumpInstr::Return(v)) },
447 ),
448 map_res(tag("hlt"), |_| -> Result<JumpInstr, ()> {
449 Ok(JumpInstr::Halt)
450 }),
451 ))(input)
452}
453
454// TODO: This is not documented in the BNF grammar.
455fn value(input: &str) -> IResult<&str, Value> {
456 alt((
457 map_res(local, |var| -> Result<Value, ()> {
458 Ok(Value::LocalVar(var))
459 }),
460 map_res(dynconstant, |cnst| -> Result<Value, ()> {
461 Ok(Value::Const(cnst))
462 }),
463 ))(input)
464}
465
466// PHI_LABELS := 'phi' ( @IDENT VAL ),
467pub fn phi_labels(input: &str) -> IResult<&str, HashMap<String, Value>> {
468 map_res(
469 preceded(
470 ws(tag("phi")),
471 separated_list1(ws(char(',')), pair(label, ws(value))),
472 ),
473 |xs: Vec<(String, Value)>| -> Result<HashMap<String, Value>, ()> {
474 Ok(HashMap::from_iter(xs.into_iter()))
475 },
476 )(input)
477}
478
479// PHI := %IDENT '=' BASETY PHI_LABELS
480pub fn phi(input: &str) -> IResult<&str, Phi> {
481 map_res(
482 tuple((local, ws(char('=')), base_type, ws(phi_labels))),
483 |(dest, _, ty, labels)| -> Result<Phi, ()> {
484 Ok(Phi {
485 ident: dest,
486 base_type: ty,
487 labels,
488 })
489 },
490 )(input)
491}
492
493// See https://c9x.me/compile/doc/il-v1.1.html#Comparisons
494pub fn compare_op(input: &str) -> IResult<&str, CmpOp> {
495 // TODO: Extend this for floating point comparisons.
496 alt((
497 str("eq", CmpOp::Eq),
498 str("ne", CmpOp::Ne),
499 str("sle", CmpOp::Sle),
500 str("slt", CmpOp::Slt),
501 str("sge", CmpOp::Sge),
502 str("sgt", CmpOp::Sgt),
503 str("ule", CmpOp::Ule),
504 str("ult", CmpOp::Ult),
505 str("uge", CmpOp::Uge),
506 str("ugt", CmpOp::Ugt),
507 ))(input)
508}
509
510fn instr_tuple<'a, F: 'a, O, E: ParseError<&'a str> + FromExternalError<&'a str, ()>>(
511 name: &'a str,
512 arg1: F,
513 arg2: F,
514) -> impl FnMut(&'a str) -> IResult<&'a str, (O, O), E>
515where
516 F: Fn(&'a str) -> IResult<&'a str, O, E>,
517{
518 pair(preceded(tag(name), ws(arg1)), preceded(char(','), ws(arg2)))
519}
520
521macro_rules! make_instr {
522 { $NAME:expr, $CONS:expr } => {
523 map_res(
524 instr_tuple($NAME, value, value),
525 |(v1, v2)| -> Result<Instr, ()> {
526 Ok($CONS(v1, v2))
527 },
528 )
529 };
530}
531
532// ALLOC_ALIGN = 4 | 8 | 16
533fn alloc_align(input: &str) -> IResult<&str, AllocAlign> {
534 alt((
535 str("4", AllocAlign::Word),
536 str("8", AllocAlign::Long),
537 str("16", AllocAlign::LongLong),
538 ))(input)
539}
540
541// See https://c9x.me/compile/doc/il-v1.1.html#Instructions
542fn instr(input: &str) -> IResult<&str, Instr> {
543 alt((
544 make_instr!("add", Instr::Add),
545 make_instr!("sub", Instr::Sub),
546 make_instr!("mul", Instr::Mul),
547 map_res(preceded(tag("neg"), ws(value)), |v| -> Result<Instr, ()> {
548 Ok(Instr::Neg(v))
549 }),
550 make_instr!("udiv", Instr::UDiv),
551 make_instr!("rem", Instr::Rem),
552 make_instr!("urem", Instr::URem),
553 make_instr!("or", Instr::Or),
554 make_instr!("xor", Instr::Xor),
555 make_instr!("and", Instr::And),
556 make_instr!("sar", Instr::Sar),
557 make_instr!("shr", Instr::Shr),
558 make_instr!("shl", Instr::Shl),
559 map_res(
560 pair(preceded(tag("load"), load_type), ws(value)),
561 |(ty, addr)| -> Result<Instr, ()> { Ok(Instr::Load(ty, addr)) },
562 ),
563 map_res(
564 pair(preceded(tag("alloc"), alloc_align), ws(parse_u64)),
565 |(align, amount)| -> Result<Instr, ()> { Ok(Instr::Alloc(align, amount)) },
566 ),
567 map_res(
568 tuple((
569 char('c'),
570 compare_op,
571 base_type,
572 ws(value),
573 char(','),
574 ws(value),
575 )),
576 |(_, op, ty, v1, _, v2)| -> Result<Instr, ()> { Ok(Instr::Compare(ty, op, v1, v2)) },
577 ),
578 map_res(
579 tuple((tag("ext"), sub_long, ws(value))),
580 |(_, ty, v)| -> Result<Instr, ()> { Ok(Instr::Ext(ty, v)) },
581 ),
582 ))(input)
583}
584
585// TODO: This is not documented in the BNF grammar.
586fn stat(input: &str) -> IResult<&str, Statement> {
587 alt((
588 map_res(
589 tuple((local, ws(char('=')), base_type, ws(instr))),
590 |(dest, _, ty, inst)| -> Result<Statement, ()> {
591 Ok(Statement::Assign(dest, ty, inst))
592 },
593 ),
594 map_res(
595 tuple((
596 preceded(tag("store"), ext_type),
597 ws(value),
598 char(','),
599 ws(value),
600 )),
601 |(ty, value, _, addr)| -> Result<Statement, ()> {
602 let instr = VolatileInstr::Store(ty, value, addr);
603 Ok(Statement::Volatile(instr))
604 },
605 ),
606 map_res(
607 tuple((
608 tag("blit"),
609 ws(value),
610 char(','),
611 ws(value),
612 char(','),
613 ws(parse_u64),
614 )),
615 |(_, src, _, dst, _, n)| -> Result<Statement, ()> {
616 let instr = VolatileInstr::Blit(src, dst, n);
617 Ok(Statement::Volatile(instr))
618 },
619 ),
620 map_res(
621 tuple((
622 local,
623 ws(char('=')),
624 abity,
625 ws(tag("call")),
626 global,
627 ws(params),
628 )),
629 |(dest, _, ty, _, fname, params)| -> Result<Statement, ()> {
630 Ok(Statement::Call(dest, ty, fname, params))
631 },
632 ),
633 ))(input)
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639
640 #[test]
641 fn test_string() {
642 assert_eq!(string("\"foobar\""), Ok(("", String::from("foobar"))));
643 assert_eq!(string("\"\""), Ok(("", String::from(""))));
644 }
645
646 #[test]
647 fn test_linkage() {
648 // Primitive linkage directives
649 assert_eq!(linkage("export"), Ok(("", Linkage::Export)));
650 assert_eq!(linkage("thread"), Ok(("", Linkage::Thread)));
651
652 // Section linkage directive
653 assert_eq!(
654 linkage("section \"foobar\"\n"),
655 Ok(("", Linkage::Section(String::from("foobar"), None)))
656 );
657 assert_eq!(
658 linkage(" section \"foo\" \"bar\" \n"),
659 Ok((
660 "",
661 Linkage::Section(String::from("foo"), Some(String::from("bar")))
662 ))
663 );
664 }
665
666 #[test]
667 fn test_types() {
668 // Base types
669 assert_eq!(base_type("w"), Ok(("", BaseType::Word)));
670 assert_eq!(base_type("l"), Ok(("", BaseType::Long)));
671 assert_eq!(base_type("s"), Ok(("", BaseType::Single)));
672 assert_eq!(base_type("d"), Ok(("", BaseType::Double)));
673
674 // Extented types
675 assert_eq!(ext_type("s"), Ok(("", ExtType::Base(BaseType::Single))));
676 assert_eq!(ext_type("h"), Ok(("", ExtType::Halfword)));
677 }
678
679 #[test]
680 fn test_ident() {
681 assert_eq!(ident("_foo"), Ok(("", String::from("_foo"))));
682 assert_eq!(ident("foobar"), Ok(("", String::from("foobar"))));
683 assert_eq!(ident("foo$_.23__"), Ok(("", String::from("foo$_.23__"))));
684 }
685
686 #[test]
687 fn test_constant() {
688 // Number
689 assert_eq!(constant("23"), Ok(("", Const::Number(23))));
690 assert_eq!(constant("-5"), Ok(("", Const::Number(-5))));
691
692 // Identifier
693 assert_eq!(
694 constant("$foobar"),
695 Ok(("", Const::Global(String::from("foobar"))))
696 );
697 assert_eq!(
698 constant("$_fo$$r"),
699 Ok(("", Const::Global(String::from("_fo$$r"))))
700 );
701
702 // Float
703 assert_eq!(constant("s_23.42"), Ok(("", Const::SFP(23.42))));
704 assert_eq!(constant("s_23."), Ok(("", Const::SFP(23.))));
705
706 // Double
707 assert_eq!(constant("d_11e-1"), Ok(("", Const::DFP(1.1))));
708 }
709
710 #[test]
711 fn test_dynconst() {
712 // Constant
713 assert_eq!(
714 dynconstant("- 23"),
715 Ok(("", DynConst::Const(Const::Number(-23))))
716 );
717 assert_eq!(
718 dynconstant("234223422342"),
719 Ok(("", DynConst::Const(Const::Number(234223422342))))
720 );
721
722 // Thread-local symbol
723 assert_eq!(
724 dynconstant("thread $foo"),
725 Ok(("", DynConst::Thread(String::from("foo"))))
726 );
727 assert_eq!(
728 dynconstant("thread $foo"),
729 Ok(("", DynConst::Thread(String::from("foo"))))
730 );
731 }
732
733 #[test]
734 fn test_members() {
735 assert_eq!(
736 members("s 23"),
737 Ok((
738 "",
739 vec![(SubType::ExtType(ExtType::Base(BaseType::Single)), 23)]
740 ))
741 );
742
743 assert_eq!(
744 members("s, s, d, d"),
745 Ok((
746 "",
747 vec![
748 (SubType::ExtType(ExtType::Base(BaseType::Single)), 1),
749 (SubType::ExtType(ExtType::Base(BaseType::Single)), 1),
750 (SubType::ExtType(ExtType::Base(BaseType::Double)), 1),
751 (SubType::ExtType(ExtType::Base(BaseType::Double)), 1),
752 ]
753 ))
754 );
755 }
756
757 #[test]
758 fn test_aggregate() {
759 // TODO: Expand
760 assert_eq!(
761 typedef("type :fourfloats = { s, s, d, d }"),
762 Ok((
763 "",
764 TypeDef {
765 name: String::from("fourfloats"),
766 defn: AggregateType::Regular(
767 None,
768 vec![
769 (SubType::ExtType(ExtType::Base(BaseType::Single)), 1),
770 (SubType::ExtType(ExtType::Base(BaseType::Single)), 1),
771 (SubType::ExtType(ExtType::Base(BaseType::Double)), 1),
772 (SubType::ExtType(ExtType::Base(BaseType::Double)), 1),
773 ]
774 )
775 }
776 ))
777 );
778
779 assert_eq!(
780 typedef("type :opaque = align 16 { 32 }"),
781 Ok((
782 "",
783 TypeDef {
784 name: String::from("opaque"),
785 defn: AggregateType::Opaque(16, 32),
786 }
787 ))
788 );
789 }
790
791 #[test]
792 fn test_dataitm() {
793 // Symbolic with and without offset
794 assert_eq!(
795 data_item("$foo"),
796 Ok(("", DataItem::Symbol(String::from("foo"), None)))
797 );
798 assert_eq!(
799 data_item("$foo +23"),
800 Ok(("", DataItem::Symbol(String::from("foo"), Some(23))))
801 );
802 assert_eq!(
803 data_item("$foo + 23"),
804 Ok(("", DataItem::Symbol(String::from("foo"), Some(23))))
805 );
806
807 // String
808 assert_eq!(
809 data_item("\"test\""),
810 Ok(("", DataItem::String(String::from("test"))))
811 );
812 assert_eq!(
813 data_item("\" f o o\""),
814 Ok(("", DataItem::String(String::from(" f o o"))))
815 );
816
817 // Constant
818 assert_eq!(
819 data_item("23"),
820 Ok(("", DataItem::Const(Const::Number(23))))
821 );
822 assert_eq!(
823 data_item("s_42"),
824 Ok(("", DataItem::Const(Const::SFP(42.))))
825 );
826 }
827
828 #[test]
829 fn test_dataobj() {
830 // TODO: Expand
831 assert_eq!(
832 data_obj("h 23"),
833 Ok((
834 "",
835 DataObj::DataItem(ExtType::Halfword, vec![DataItem::Const(Const::Number(23))])
836 ))
837 );
838 assert_eq!(data_obj("z 42"), Ok(("", DataObj::ZeroFill(42))));
839 }
840
841 #[test]
842 fn test_dataobjs() {
843 // TODO: Expand
844 assert_eq!(data_objs("z 1337"), Ok(("", vec![DataObj::ZeroFill(1337)])));
845 assert_eq!(
846 data_objs("z 42, z 10"),
847 Ok(("", vec![DataObj::ZeroFill(42), DataObj::ZeroFill(10)]))
848 );
849 }
850
851 #[test]
852 fn test_datadef() {
853 let input = "data $b = { z 1000 }";
854 let items = vec![DataObj::ZeroFill(1000)];
855 assert_eq!(
856 datadef(input),
857 Ok((
858 "",
859 DataDef {
860 linkage: vec![],
861 name: String::from("b"),
862 align: None,
863 objs: items
864 }
865 ))
866 );
867
868 let input = "data $c = { l -1 2, l $c }";
869 let items = vec![
870 DataObj::DataItem(
871 ExtType::Base(BaseType::Long),
872 vec![
873 DataItem::Const(Const::Number(-1)),
874 DataItem::Const(Const::Number(2)),
875 ],
876 ),
877 DataObj::DataItem(
878 ExtType::Base(BaseType::Long),
879 vec![DataItem::Symbol(String::from("c"), None)],
880 ),
881 ];
882 assert_eq!(
883 datadef(input),
884 Ok((
885 "",
886 DataDef {
887 linkage: vec![],
888 name: String::from("c"),
889 align: None,
890 objs: items
891 }
892 ))
893 );
894 }
895
896 #[test]
897 fn test_block() {
898 assert_eq!(
899 block("@start\nhlt\n"),
900 Ok((
901 "",
902 Block {
903 label: String::from("start"),
904 phi: vec![],
905 inst: vec![],
906 jump: Some(JumpInstr::Halt),
907 }
908 ))
909 );
910
911 assert_eq!(
912 block("@start\n"),
913 Ok((
914 "",
915 Block {
916 label: String::from("start"),
917 phi: vec![],
918 inst: vec![],
919 jump: None,
920 }
921 ))
922 );
923
924 assert_eq!(
925 block("@.1\n%.10 =w phi @body.10 0, @logic_right.11 %foo\nret\n"),
926 Ok((
927 "",
928 Block {
929 label: String::from(".1"),
930 phi: vec![Phi {
931 ident: String::from(".10"),
932 base_type: BaseType::Word,
933 labels: HashMap::from([
934 (
935 String::from("body.10"),
936 Value::Const(DynConst::Const(Const::Number(0)))
937 ),
938 (
939 String::from("logic_right.11"),
940 Value::LocalVar(String::from("foo")),
941 )
942 ])
943 }],
944 inst: vec![],
945 jump: Some(JumpInstr::Return(None)),
946 }
947 ))
948 );
949 }
950
951 #[test]
952 fn test_stat() {
953 assert_eq!(
954 stat("%c =w add %a, %b"),
955 Ok((
956 "",
957 Statement::Assign(
958 String::from("c"),
959 BaseType::Word,
960 Instr::Add(
961 Value::LocalVar(String::from("a")),
962 Value::LocalVar(String::from("b"))
963 )
964 )
965 ))
966 );
967
968 assert_eq!(
969 stat("%r =w ceqw %o1, %o2"),
970 Ok((
971 "",
972 Statement::Assign(
973 String::from("r"),
974 BaseType::Word,
975 Instr::Compare(
976 BaseType::Word,
977 CmpOp::Eq,
978 Value::LocalVar(String::from("o1")),
979 Value::LocalVar(String::from("o2"))
980 )
981 )
982 ))
983 );
984
985 assert_eq!(
986 stat("blit %s0, %s1, 8"),
987 Ok((
988 "",
989 Statement::Volatile(VolatileInstr::Blit(
990 Value::LocalVar(String::from("s0")),
991 Value::LocalVar(String::from("s1")),
992 8
993 ))
994 ))
995 );
996
997 assert_eq!(
998 stat("%r =s call $myfunc(s %a, l %ap)"),
999 Ok((
1000 "",
1001 Statement::Call(
1002 String::from("r"),
1003 Type::Base(BaseType::Single),
1004 String::from("myfunc"),
1005 vec![
1006 FuncParam::Regular(Type::Base(BaseType::Single), String::from("a")),
1007 FuncParam::Regular(Type::Base(BaseType::Long), String::from("ap")),
1008 ],
1009 )
1010 ))
1011 );
1012 }
1013
1014 #[test]
1015 fn test_funcdef() {
1016 let input = "export function w $getone(:one %p) {\n@start\nhlt\n}";
1017 assert_eq!(
1018 funcdef(input),
1019 Ok((
1020 "",
1021 FuncDef {
1022 linkage: vec![Linkage::Export],
1023 name: String::from("getone"),
1024 abity: Some(Type::Base(BaseType::Word)),
1025 params: vec![FuncParam::Regular(
1026 Type::UserDef(String::from("one")),
1027 String::from("p")
1028 )],
1029 body: vec![Block {
1030 label: String::from("start"),
1031 phi: vec![],
1032 inst: vec![],
1033 jump: Some(JumpInstr::Halt),
1034 }]
1035 }
1036 ))
1037 );
1038 }
1039}