1use crate::types::*;2use crate::util::*;3use std::collections::HashMap;45use 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};1617// 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}2526// See https://c9x.me/git/qbe.git/tree/parse.c?h=v1.1#n30227fn 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}3637// See https://c9x.me/compile/doc/il-v1.1.html#Sigils38fn 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}5051////////////////////////////////////////////////////////////////////////5253// 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}6263// 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}7374// CONST :=75// ['-'] NUMBER # Decimal integer76// | 's_' FP # Single-precision float77// | 'd_' FP # Double-precision float78// | $IDENT # Global symbol79fn 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#n24582 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}103104// DYNCONST :=105// CONST106// | 'thread' $IDENT # Thread-local symbol107fn 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}118119// LINKAGE :=120// 'export'121// | 'thread'122// | 'section' SECNAME123// | 'section' SECNAME SECFLAGS124//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}143144// Parse linkage terminated with one or more newline characters.145fn linkage(input: &str) -> IResult<&str, Linkage> {146 terminated(ws(_linkage), newline0)(input)147}148149// TYPEDEF := REGTY | OPAQUETY150pub fn typedef(input: &str) -> IResult<&str, TypeDef> {151 alt((regty, opaque))(input)152}153154// 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}176177// SUBTY := EXTTY | :IDENT178fn 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}188189// MEMBERS := ( MEMBER ),190fn members(input: &str) -> IResult<&str, Vec<(SubType, u64)>> {191 separated_list1(ws(char(',')), member)(input)192}193194// 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}201202// 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}221222// 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}249250// DATAOBJS := ( DATAOBJ ),251fn data_objs(input: &str) -> IResult<&str, Vec<DataObj>> {252 separated_list1(ws(char(',')), data_obj)(input)253}254255// DATAOBJ := EXTTY DATAITEM+256// | 'z' NUMBER257fn 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}269270// DATAITEM :=271// $IDENT ['+' NUMBER] # Symbol and offset272// | '"' ... '"' # String273// | CONST # Constant274fn 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}288289// FUNCDEF :=290// LINKAGE*291// 'function' [ABITY] $IDENT PARAMS [NL]292// '{' NL293// BODY294// '}'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}317318// 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}327328fn 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}337338// See https://c9x.me/compile/doc/il-v1.1.html#Memory339fn 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}349350// ABITY := BASETY | SUBWTY | :IDENT351fn 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}362363// PARAM :=364// ABITY %IDENT # Regular parameter365// | 'env' %IDENT # Environment parameter (first)366// | '...' # Variadic marker (last)367fn param(input: &str) -> IResult<&str, FuncParam> {368 // TODO: Ensure that env is the first parameter369 // TODO: Ensure that variadic marker is the last parameter370 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}384385// 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}392393// BODY = BLOCK+394fn body(input: &str) -> IResult<&str, Vec<Block>> {395 many1(block)(input)396}397398// BLOCK :=399// @IDENT NL # Block label400// ( PHI NL )* # Phi instructions401// ( INST NL )* # Regular instructions402// JUMP NL # Jump or return403fn 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}421422// JUMP :=423// 'jmp' @IDENT # Unconditional424// | 'jnz' VAL, @IDENT, @IDENT # Conditional425// | 'ret' [VAL] # Return426// | 'hlt' # Termination427fn 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}453454// 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}465466// 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}478479// PHI := %IDENT '=' BASETY PHI_LABELS480pub 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}492493// See https://c9x.me/compile/doc/il-v1.1.html#Comparisons494pub 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}509510fn 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>515where516 F: Fn(&'a str) -> IResult<&'a str, O, E>,517{518 pair(preceded(tag(name), ws(arg1)), preceded(char(','), ws(arg2)))519}520521macro_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}531532// ALLOC_ALIGN = 4 | 8 | 16533fn 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}540541// See https://c9x.me/compile/doc/il-v1.1.html#Instructions542fn 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}584585// 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}635636#[cfg(test)]637mod tests {638 use super::*;639640 #[test]641 fn test_string() {642 assert_eq!(string("\"foobar\""), Ok(("", String::from("foobar"))));643 assert_eq!(string("\"\""), Ok(("", String::from(""))));644 }645646 #[test]647 fn test_linkage() {648 // Primitive linkage directives649 assert_eq!(linkage("export"), Ok(("", Linkage::Export)));650 assert_eq!(linkage("thread"), Ok(("", Linkage::Thread)));651652 // Section linkage directive653 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 }665666 #[test]667 fn test_types() {668 // Base types669 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)));673674 // Extented types675 assert_eq!(ext_type("s"), Ok(("", ExtType::Base(BaseType::Single))));676 assert_eq!(ext_type("h"), Ok(("", ExtType::Halfword)));677 }678679 #[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 }685686 #[test]687 fn test_constant() {688 // Number689 assert_eq!(constant("23"), Ok(("", Const::Number(23))));690 assert_eq!(constant("-5"), Ok(("", Const::Number(-5))));691692 // Identifier693 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 );701702 // Float703 assert_eq!(constant("s_23.42"), Ok(("", Const::SFP(23.42))));704 assert_eq!(constant("s_23."), Ok(("", Const::SFP(23.))));705706 // Double707 assert_eq!(constant("d_11e-1"), Ok(("", Const::DFP(1.1))));708 }709710 #[test]711 fn test_dynconst() {712 // Constant713 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 );721722 // Thread-local symbol723 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 }732733 #[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 );742743 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 }756757 #[test]758 fn test_aggregate() {759 // TODO: Expand760 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 );778779 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 }790791 #[test]792 fn test_dataitm() {793 // Symbolic with and without offset794 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 );806807 // String808 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 );816817 // Constant818 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 }827828 #[test]829 fn test_dataobj() {830 // TODO: Expand831 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 }840841 #[test]842 fn test_dataobjs() {843 // TODO: Expand844 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 }850851 #[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: items864 }865 ))866 );867868 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: items891 }892 ))893 );894 }895896 #[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 );910911 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 );923924 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 }950951 #[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 );967968 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 );984985 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 8993 ))994 ))995 );996997 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 }10131014 #[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}