1use std::fmt;
33
34use rucc_base::{Interner, Symbol};
35use rucc_target::{Constraint, PhysReg, RegClass, RegFile, Role, Segment};
36
37use crate::func::Func;
38use crate::inst::{BlockCall, Mem, Opcode, Operand, Param, Reg};
39
40#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct ParseError {
47 pub line: u32,
49 pub message: String,
51}
52
53impl fmt::Display for ParseError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "line {}: {}", self.line, self.message)
56 }
57}
58
59impl std::error::Error for ParseError {}
60
61pub fn parse(text: &str, names: &mut Interner, regs: &RegFile) -> Result<Vec<Func>, ParseError> {
70 Parser { text, pos: 0, line: 1, names, regs }.funcs()
71}
72
73#[derive(Clone, Copy, PartialEq, Eq)]
79enum Written {
80 Virtual { number: u32, class: Option<RegClass> },
82 Physical { reg: PhysReg, class: RegClass },
84}
85
86#[derive(Clone, Copy)]
88struct PendingOperand {
89 reg: Written,
90 role: Role,
91 constraint: Constraint,
92}
93
94#[derive(Clone, Copy, Default)]
96struct PendingMem {
97 base: Option<PendingOperand>,
98 index: Option<PendingOperand>,
99 scale: u8,
100 disp: i32,
101 symbol: Option<Symbol>,
102 got: bool,
103 segment: Option<Segment>,
104}
105
106struct PendingCall {
108 block: u32,
109 args: Vec<Written>,
110}
111
112struct PendingInst {
114 opcode: Symbol,
115 operands: Vec<PendingOperand>,
116 mem: Option<PendingMem>,
117 imm: Option<i64>,
118 symbol: Option<Symbol>,
119 succs: Vec<PendingCall>,
120 line: u32,
121}
122
123struct PendingBlock {
125 number: u32,
126 params: Vec<Written>,
127 insts: Vec<PendingInst>,
128 line: u32,
129}
130
131struct Parser<'a, 'n> {
133 text: &'a str,
134 pos: usize,
135 line: u32,
136 names: &'n mut Interner,
137 regs: &'n RegFile,
138}
139
140impl<'a> Parser<'a, '_> {
141 fn funcs(mut self) -> Result<Vec<Func>, ParseError> {
144 let mut funcs = Vec::new();
145 loop {
146 self.skip_blank_lines();
147 if self.at_end() {
148 return Ok(funcs);
149 }
150 funcs.push(self.func()?);
151 }
152 }
153
154 fn func(&mut self) -> Result<Func, ParseError> {
155 self.expect("mfunc")?;
156 let name = self.symbol()?;
157 self.expect("{")?;
158 self.end_of_line()?;
159
160 let mut blocks: Vec<PendingBlock> = Vec::new();
161 loop {
162 self.skip_blank_lines();
163 if self.eat("}") {
164 self.end_of_line()?;
165 break;
166 }
167 if self.at_end() {
168 return self.fail("the function is not closed");
169 }
170 blocks.push(self.block()?);
171 }
172 self.build(name, blocks)
173 }
174
175 fn block(&mut self) -> Result<PendingBlock, ParseError> {
176 let line = self.line;
177 let number = self.label()?;
178 let mut params = Vec::new();
179 if self.eat("(") {
180 loop {
181 params.push(self.written(true)?);
182 if !self.eat(",") {
183 break;
184 }
185 }
186 self.expect(")")?;
187 }
188 self.expect(":")?;
189 self.end_of_line()?;
190
191 let mut insts = Vec::new();
192 loop {
193 self.spaces();
194 if self.at_end() || self.at("\n") || self.at("}") || self.at_label() {
195 break;
196 }
197 insts.push(self.inst()?);
198 }
199 Ok(PendingBlock { number, params, insts, line })
200 }
201
202 fn inst(&mut self) -> Result<PendingInst, ParseError> {
203 let line = self.line;
204 let mut operands = Vec::new();
205 if self.at("%") || self.at("$") || self.peek_word() == "early" {
206 loop {
207 operands.push(self.operand(true)?);
208 if !self.eat(",") {
209 break;
210 }
211 }
212 self.expect("=")?;
213 }
214 let opcode = self.opcode()?;
215
216 let mut inst = PendingInst {
217 opcode,
218 operands,
219 mem: None,
220 imm: None,
221 symbol: None,
222 succs: Vec::new(),
223 line,
224 };
225 self.spaces();
226 if !self.at("\n") && !self.at_end() {
227 loop {
228 self.item(&mut inst)?;
229 if !self.eat(",") {
230 break;
231 }
232 }
233 }
234 self.end_of_line()?;
235 Ok(inst)
236 }
237
238 fn item(&mut self, inst: &mut PendingInst) -> Result<(), ParseError> {
240 self.spaces();
241 if self.at("%") || self.at("$") || self.peek_word() == "early" {
242 inst.operands.push(self.operand(false)?);
243 return Ok(());
244 }
245 if self.at("@") {
246 let symbol = self.symbol()?;
247 if inst.symbol.is_some() {
248 return self.fail("the instruction already names a symbol");
249 }
250 inst.symbol = Some(symbol);
251 return Ok(());
252 }
253 if self.at("[") {
254 let mem = self.mem()?;
255 if inst.mem.is_some() {
256 return self.fail("the instruction already has a memory operand");
257 }
258 inst.mem = Some(mem);
259 return Ok(());
260 }
261 if self.at_label() {
262 let call = self.block_call()?;
263 inst.succs.push(call);
264 return Ok(());
265 }
266 let value = self.i64()?;
267 if inst.imm.is_some() {
268 return self.fail("the instruction already has an immediate");
269 }
270 inst.imm = Some(value);
271 Ok(())
272 }
273
274 fn operand(&mut self, written: bool) -> Result<PendingOperand, ParseError> {
276 let early = self.eat_word("early");
277 if early && !written {
278 return self.fail("only an operand an instruction writes can be early");
279 }
280 let reg = self.written(written)?;
281 let role = match (written, early) {
282 (false, _) => Role::Use,
283 (true, false) => Role::Def,
284 (true, true) => Role::EarlyDef,
285 };
286 let mut constraint = Constraint::Reg;
287 if self.eat("(") {
288 constraint = self.constraint()?;
289 self.expect(")")?;
290 }
291 Ok(PendingOperand { reg, role, constraint })
292 }
293
294 fn constraint(&mut self) -> Result<Constraint, ParseError> {
295 if self.at("$") {
296 let Written::Physical { reg, .. } = self.written(false)? else {
297 return self.fail("a register is fixed to a physical register");
298 };
299 return Ok(Constraint::Fixed(reg));
300 }
301 match self.word() {
302 "any" => Ok(Constraint::Any),
303 "stack" => Ok(Constraint::Stack),
304 "reuse" => {
305 let at = self.u32()?;
306 match u8::try_from(at) {
307 Ok(at) => Ok(Constraint::Reuse(at)),
308 Err(_) => self.fail(format!("no instruction has {at} operands")),
309 }
310 }
311 other => self.fail(format!("`{other}` is not something an operand can be")),
312 }
313 }
314
315 fn written(&mut self, declared: bool) -> Result<Written, ParseError> {
320 self.spaces();
321 if self.eat("$") {
322 let name = self.glued_word();
323 return match self.regs.reg_named(name) {
324 Some((class, reg)) => Ok(Written::Physical { reg, class }),
325 None => self.fail(format!("this target has no register called `{name}`")),
326 };
327 }
328 self.expect("%")?;
329 let number = self.u32()?;
330 if !declared {
331 return Ok(Written::Virtual { number, class: None });
332 }
333 self.expect(":")?;
334 let name = self.word();
335 match self.regs.class_named(name) {
336 Some(class) => Ok(Written::Virtual { number, class: Some(class) }),
337 None => self.fail(format!("this target has no register class called `{name}`")),
338 }
339 }
340
341 fn mem(&mut self) -> Result<PendingMem, ParseError> {
349 self.expect("[")?;
350 let mut mem = PendingMem { scale: 1, ..PendingMem::default() };
351 mem.got = self.eat_word("got");
354 mem.segment = if self.eat("fs:") {
358 Some(Segment::Fs)
359 } else if self.eat("gs:") {
360 Some(Segment::Gs)
361 } else {
362 None
363 };
364 let mut negative = false;
365 loop {
366 self.spaces();
367 if self.at("@") {
368 if mem.symbol.is_some() {
369 return self.fail("an address names one symbol");
370 }
371 mem.symbol = Some(self.symbol()?);
372 } else if self.at("%") || self.at("$") {
373 let operand = PendingOperand {
374 reg: self.written(false)?,
375 role: Role::Use,
376 constraint: Constraint::Reg,
377 };
378 if mem.base.is_none() {
379 mem.base = Some(operand);
380 } else if mem.index.is_none() {
381 mem.index = Some(operand);
382 if self.eat("*") {
383 let scale = self.u32()?;
384 match u8::try_from(scale) {
385 Ok(scale) => mem.scale = scale,
386 Err(_) => return self.fail(format!("{scale} is not a scale")),
387 }
388 }
389 } else {
390 return self.fail("an address names at most two registers");
391 }
392 } else {
393 let disp = self.i64()?;
394 let disp = if negative { -disp } else { disp };
395 match i32::try_from(disp) {
396 Ok(disp) => mem.disp = disp,
397 Err(_) => return self.fail(format!("{disp} is too far for a displacement")),
398 }
399 }
400 if self.eat("+") {
401 negative = false;
402 } else if self.eat("-") {
403 negative = true;
404 } else {
405 break;
406 }
407 }
408 self.expect("]")?;
409 Ok(mem)
410 }
411
412 fn block_call(&mut self) -> Result<PendingCall, ParseError> {
414 let block = self.label()?;
415 let mut args = Vec::new();
416 if self.eat("(") {
417 loop {
418 args.push(self.written(false)?);
421 if !self.eat(",") {
422 break;
423 }
424 }
425 self.expect(")")?;
426 }
427 Ok(PendingCall { block, args })
428 }
429
430 fn build(&mut self, name: Symbol, pending: Vec<PendingBlock>) -> Result<Func, ParseError> {
433 let mut func = Func::new(name);
434 for (index, block) in pending.iter().enumerate() {
435 if block.number as usize != index {
436 self.line = block.line;
437 return self.fail(format!(
438 "this is block {index} of the function and the text calls it block{}",
439 block.number
440 ));
441 }
442 func.create_block();
443 }
444 let blocks: Vec<_> = func.blocks().collect();
445
446 let mut next = 0;
449 for (block, read) in blocks.iter().zip(&pending) {
450 for param in &read.params {
451 match *param {
452 Written::Virtual { number, class } => {
453 self.line = read.line;
454 self.expect_number(number, next)?;
455 let Some(class) = class else {
456 return self
457 .fail(format!("%{number} arrives without saying its class"));
458 };
459 func.append_param(*block, class);
460 next += 1;
461 }
462 Written::Physical { reg, class } => {
463 func.append_given_param(*block, Param { reg: Reg::physical(reg), class });
464 }
465 }
466 }
467 for inst in &read.insts {
468 for operand in &inst.operands {
469 if operand.role == Role::Use {
470 continue;
471 }
472 if let Written::Virtual { number, class } = operand.reg {
473 self.line = inst.line;
474 self.expect_number(number, next)?;
475 let Some(class) = class else {
476 return self.fail(format!("%{number} is written without a class"));
477 };
478 func.new_vreg(class);
479 next += 1;
480 }
481 }
482 }
483 }
484
485 for (block, read) in blocks.iter().zip(&pending) {
486 let last = read.insts.len().saturating_sub(1);
487 for (at, inst) in read.insts.iter().enumerate() {
488 self.line = inst.line;
489 if !inst.succs.is_empty() && at != last {
490 return self.fail("only the last instruction of a block says where it goes");
491 }
492 let mut operands = Vec::new();
495 for operand in &inst.operands {
496 operands.push(self.resolve(&func, operand)?);
497 }
498 let mem = match inst.mem {
499 Some(mem) => Some(Mem {
500 base: match mem.base {
501 Some(operand) => Some(self.resolve(&func, &operand)?),
502 None => None,
503 },
504 index: match mem.index {
505 Some(operand) => Some(self.resolve(&func, &operand)?),
506 None => None,
507 },
508 scale: mem.scale,
509 disp: mem.disp,
510 symbol: mem.symbol,
511 got: mem.got,
512 segment: mem.segment,
513 }),
514 None => None,
515 };
516 let mut builder = func.build(*block, Opcode::new(inst.opcode));
517 for operand in operands {
518 builder = builder.operand(operand);
519 }
520 if let Some(mem) = mem {
521 builder = builder.mem(mem);
522 }
523 if let Some(symbol) = inst.symbol {
524 builder = builder.symbol(symbol);
525 }
526 if let Some(value) = inst.imm {
527 builder = builder.imm(value);
528 }
529 builder.finish();
530 }
531 let Some(terminator) = read.insts.last() else { continue };
532 self.line = terminator.line;
533 let mut succs = Vec::new();
534 for call in &terminator.succs {
535 let Some(&target) = blocks.get(call.block as usize) else {
536 return self
537 .fail(format!("block{} is branched to and never begins", call.block));
538 };
539 let mut args = Vec::new();
540 for (at, arg) in call.args.iter().enumerate() {
541 args.push(match *arg {
542 Written::Physical { reg, .. } => Reg::physical(reg),
543 Written::Virtual { number, .. } => {
544 let class = func[target].params.get(at).map(|param| param.class);
545 let Some(class) = class else {
546 return self.fail(format!(
547 "block{} takes {} arguments and is given more",
548 call.block,
549 func[target].params.len()
550 ));
551 };
552 self.virtual_reg(&func, number, class)?
553 }
554 });
555 }
556 if args.len() != func[target].params.len() {
557 return self.fail(format!(
558 "block{} takes {} arguments and is given {}",
559 call.block,
560 func[target].params.len(),
561 args.len()
562 ));
563 }
564 succs.push(BlockCall::with(target, args));
565 }
566 *func.succs_mut(*block) = succs;
567 }
568 Ok(func)
569 }
570
571 fn resolve(&self, func: &Func, operand: &PendingOperand) -> Result<Operand, ParseError> {
578 let (reg, class) = match operand.reg {
579 Written::Physical { reg, class } => (Reg::physical(reg), class),
580 Written::Virtual { number, class } => {
581 let reg = Reg::virtual_reg(number);
582 match (class, func.class_of(reg)) {
583 (Some(class), _) => (reg, class),
584 (None, Some(class)) => (reg, class),
585 (None, None) => {
586 return self.fail(format!("%{number} is read and never written"));
587 }
588 }
589 }
590 };
591 Ok(Operand { reg, class, role: operand.role, constraint: operand.constraint })
592 }
593
594 fn virtual_reg(&self, func: &Func, number: u32, class: RegClass) -> Result<Reg, ParseError> {
596 let reg = Reg::virtual_reg(number);
597 match func.class_of(reg) {
598 None => self.fail(format!("%{number} is read and never written")),
599 Some(found) if found != class => {
600 self.fail(format!("%{number} is passed to a parameter of another class"))
601 }
602 Some(_) => Ok(reg),
603 }
604 }
605
606 fn expect_number(&self, number: u32, next: u32) -> Result<(), ParseError> {
608 if number == next {
609 return Ok(());
610 }
611 self.fail(format!("this is %{next} of the function and the text calls it %{number}"))
612 }
613
614 fn fail<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
617 Err(ParseError { line: self.line, message: message.into() })
618 }
619
620 fn at(&self, text: &str) -> bool {
621 self.text[self.pos..].starts_with(text)
622 }
623
624 fn at_end(&self) -> bool {
625 self.pos >= self.text.len()
626 }
627
628 fn at_label(&self) -> bool {
630 let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
631 let Some(rest) = rest.strip_prefix("block") else { return false };
632 rest.starts_with(|c: char| c.is_ascii_digit())
633 }
634
635 fn peek(&self) -> Option<u8> {
636 self.text.as_bytes().get(self.pos).copied()
637 }
638
639 fn spaces(&mut self) {
640 while matches!(self.peek(), Some(b' ' | b'\t')) {
641 self.pos += 1;
642 }
643 }
644
645 fn eat(&mut self, text: &str) -> bool {
646 self.spaces();
647 if self.at(text) {
648 self.pos += text.len();
649 return true;
650 }
651 false
652 }
653
654 fn expect(&mut self, text: &str) -> Result<(), ParseError> {
655 if self.eat(text) {
656 return Ok(());
657 }
658 self.fail(format!("expected `{text}`"))
659 }
660
661 fn word(&mut self) -> &'a str {
662 self.spaces();
663 self.glued_word()
664 }
665
666 fn glued_word(&mut self) -> &'a str {
668 let start = self.pos;
669 while self.peek().is_some_and(is_name_byte) {
670 self.pos += 1;
671 }
672 &self.text[start..self.pos]
673 }
674
675 fn peek_word(&self) -> &'a str {
676 let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
677 let end = rest
678 .find(|c: char| !(c.is_ascii_alphanumeric() || c == '_' || c == '.'))
679 .unwrap_or(rest.len());
680 &rest[..end]
681 }
682
683 fn eat_word(&mut self, word: &str) -> bool {
684 if self.peek_word() != word {
685 return false;
686 }
687 self.word();
688 true
689 }
690
691 fn label(&mut self) -> Result<u32, ParseError> {
693 self.expect("block")?;
694 self.u32()
695 }
696
697 fn symbol(&mut self) -> Result<Symbol, ParseError> {
699 self.expect("@")?;
700 let name = self.glued_word();
701 if name.is_empty() {
702 return self.fail("a symbol has a name");
703 }
704 Ok(self.names.intern(name))
705 }
706
707 fn u32(&mut self) -> Result<u32, ParseError> {
708 let word = self.word();
709 match word.parse::<u32>() {
710 Ok(number) => Ok(number),
711 Err(_) => self.fail(format!("`{word}` is not a number")),
712 }
713 }
714
715 fn i64(&mut self) -> Result<i64, ParseError> {
716 self.spaces();
717 let start = self.pos;
718 if self.at("-") {
719 self.pos += 1;
720 }
721 self.glued_word();
722 let word = &self.text[start..self.pos];
723 match word.parse::<i64>() {
724 Ok(number) => Ok(number),
725 Err(_) => self.fail(format!("`{word}` is not a number")),
726 }
727 }
728
729 fn opcode(&mut self) -> Result<Symbol, ParseError> {
731 let word = self.word();
732 if word.is_empty() {
733 return self.fail("expected an instruction");
734 }
735 Ok(self.names.intern(word))
736 }
737
738 fn end_of_line(&mut self) -> Result<(), ParseError> {
739 self.spaces();
740 if self.at_end() {
741 return Ok(());
742 }
743 if !self.at("\n") {
744 let rest = self.text[self.pos..].lines().next().unwrap_or_default();
745 return self.fail(format!("`{rest}` is left over at the end of the line"));
746 }
747 self.pos += 1;
748 self.line += 1;
749 Ok(())
750 }
751
752 fn skip_blank_lines(&mut self) {
753 loop {
754 let held = self.pos;
755 self.spaces();
756 if self.at("\n") {
757 self.pos += 1;
758 self.line += 1;
759 continue;
760 }
761 self.pos = held;
762 return;
763 }
764 }
765}
766
767fn is_name_byte(byte: u8) -> bool {
769 byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'.'
770}
771
772#[cfg(test)]
773mod tests {
774 use super::*;
775 use crate::fixtures::{AFTER, BEFORE, REGS};
776 use crate::print::{print, print_func};
777
778 fn round_trip(text: &str) {
780 let mut names = Interner::new();
781 let funcs = parse(text, &mut names, ®S).expect("the fixture is what the printer writes");
782 let [func] = &funcs[..] else { panic!("the text is one function") };
783 assert_eq!(print_func(func, &names, ®S), text);
784 }
785
786 fn error(text: &str) -> String {
788 let mut names = Interner::new();
789 parse(text, &mut names, ®S).expect_err("the text does not add up").to_string()
790 }
791
792 #[test]
793 fn a_function_before_allocation_round_trips() {
794 round_trip(BEFORE);
795 }
796
797 #[test]
798 fn a_function_after_allocation_round_trips() {
799 round_trip(AFTER);
800 }
801
802 #[test]
803 fn an_address_read_out_of_the_offset_table_round_trips() {
804 round_trip(
808 "\
809mfunc @take {
810block0:
811 %0:gpr = x64.mov_rm [got @away]
812 x64.ret
813}
814",
815 );
816 }
817
818 #[test]
819 fn an_address_in_a_thread_s_own_block_round_trips() {
820 round_trip(
824 "\
825mfunc @guard {
826block0:
827 %0:gpr = x64.mov_rm [fs:40]
828 x64.ret
829}
830",
831 );
832 }
833
834 #[test]
835 fn an_instruction_of_an_immediate_and_an_address_round_trips() {
836 round_trip(
841 "\
842mfunc @deep {
843block0:
844 x64.or_mi_8 [$rsp], 0
845 x64.ret
846}
847",
848 );
849 }
850
851 #[test]
852 fn two_functions_round_trip() {
853 let text = format!("{BEFORE}\n{AFTER}");
854 let mut names = Interner::new();
855 let funcs = parse(&text, &mut names, ®S).expect("both fixtures are readable");
856 assert_eq!(funcs.len(), 2);
857 assert_eq!(print(&funcs, &names, ®S), text);
858 }
859
860 #[test]
861 fn an_empty_text_is_no_functions() {
862 let mut names = Interner::new();
863 assert!(parse("\n\n", &mut names, ®S).expect("nothing is readable").is_empty());
864 }
865
866 #[test]
867 fn a_register_nothing_writes_is_refused() {
868 let text = "mfunc @f {\nblock0:\n x64.ret %3\n}\n";
869 assert_eq!(error(text), "line 3: %3 is read and never written");
870 }
871
872 #[test]
873 fn a_register_numbered_out_of_order_is_refused() {
874 let text = "mfunc @f {\nblock0:\n %1:gpr = x64.mov_ri 4\n x64.ret %1\n}\n";
875 assert_eq!(error(text), "line 3: this is %0 of the function and the text calls it %1");
876 }
877
878 #[test]
879 fn a_block_numbered_out_of_order_is_refused() {
880 let text = "mfunc @f {\nblock1:\n x64.ret $rax\n}\n";
881 assert_eq!(
882 error(text),
883 "line 2: this is block 0 of the function and the text calls it block1"
884 );
885 }
886
887 #[test]
888 fn a_register_the_target_does_not_have_is_refused() {
889 let text = "mfunc @f {\nblock0:\n x64.ret $r13\n}\n";
890 assert_eq!(error(text), "line 3: this target has no register called `r13`");
891 }
892
893 #[test]
894 fn a_class_the_target_does_not_have_is_refused() {
895 let text = "mfunc @f {\nblock0:\n %0:vec = x64.mov_ri 4\n x64.ret $rax\n}\n";
896 assert_eq!(error(text), "line 3: this target has no register class called `vec`");
897 }
898
899 #[test]
900 fn an_argument_of_another_class_is_refused() {
901 let text = "\
902mfunc @f {
903block0:
904 %0:xmm = x64.movd_xr $rax
905 x64.jmp block1(%0)
906
907block1(%1:gpr):
908 x64.ret $rax
909}
910";
911 assert_eq!(error(text), "line 4: %0 is passed to a parameter of another class");
912 }
913
914 #[test]
915 fn a_block_given_the_wrong_number_of_arguments_is_refused() {
916 let text = "\
917mfunc @f {
918block0(%0:gpr):
919 x64.jmp block1(%0)
920
921block1(%1:gpr, %2:gpr):
922 x64.ret $rax
923}
924";
925 assert_eq!(error(text), "line 3: block1 takes 2 arguments and is given 1");
926 }
927
928 #[test]
929 fn a_branch_to_a_block_that_never_begins_is_refused() {
930 let text = "mfunc @f {\nblock0:\n x64.jmp block4\n}\n";
931 assert_eq!(error(text), "line 3: block4 is branched to and never begins");
932 }
933
934 #[test]
935 fn a_branch_before_the_end_of_a_block_is_refused() {
936 let text = "mfunc @f {\nblock0:\n x64.jmp block0\n x64.ret $rax\n}\n";
937 assert_eq!(error(text), "line 3: only the last instruction of a block says where it goes");
938 }
939
940 #[test]
941 fn a_read_operand_cannot_be_early() {
942 let text = "mfunc @f {\nblock0:\n x64.ret early $rax\n}\n";
943 assert_eq!(error(text), "line 3: only an operand an instruction writes can be early");
944 }
945
946 #[test]
947 fn a_second_immediate_is_refused() {
948 let text = "mfunc @f {\nblock0:\n x64.ret 1, 2\n}\n";
949 assert_eq!(error(text), "line 3: the instruction already has an immediate");
950 }
951
952 #[test]
953 fn a_function_that_does_not_close_is_refused() {
954 let text = "mfunc @f {\nblock0:\n x64.ret $rax\n";
955 assert_eq!(error(text), "line 4: the function is not closed");
956 }
957
958 #[test]
959 fn what_is_left_over_on_a_line_is_reported() {
960 let text = "mfunc @f {\nblock0:\n x64.ret $rax ]\n}\n";
961 assert_eq!(error(text), "line 3: `]` is left over at the end of the line");
962 }
963}